diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/mozapps/update/tests/data | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/mozapps/update/tests/data')
31 files changed, 8449 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/tests/data/app_update.sjs b/toolkit/mozapps/update/tests/data/app_update.sjs new file mode 100644 index 0000000000..2081118547 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/app_update.sjs @@ -0,0 +1,251 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +/** + * Server side http server script for application update tests. + */ + +// Definitions from test and other files used by the tests +/* global getState */ + +function getTestDataFile(aFilename) { + let file = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + let pathParts = REL_PATH_DATA.split("/"); + for (let i = 0; i < pathParts.length; ++i) { + file.append(pathParts[i]); + } + if (aFilename) { + file.append(aFilename); + } + return file; +} + +function loadHelperScript(aScriptFile) { + let scriptSpec = Services.io.newFileURI(aScriptFile).spec; + Services.scriptloader.loadSubScript(scriptSpec, this); +} + +var scriptFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); +scriptFile.initWithPath(getState("__LOCATION__")); +scriptFile = scriptFile.parent; +/* import-globals-from ../browser/testConstants.js */ +scriptFile.append("testConstants.js"); +loadHelperScript(scriptFile); + +/* import-globals-from sharedUpdateXML.js */ +scriptFile = getTestDataFile("sharedUpdateXML.js"); +loadHelperScript(scriptFile); + +const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR; +const BAD_SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + "not_here.mar"; + +// A value of 10 caused the tests to intermittently fail on Mac OS X so be +// careful when changing this value. +const SLOW_RESPONSE_INTERVAL = 100; +const MAX_SLOW_RESPONSE_RETRIES = 200; +var gSlowDownloadTimer; +var gSlowCheckTimer; + +function handleRequest(aRequest, aResponse) { + let params = {}; + if (aRequest.queryString) { + params = parseQueryString(aRequest.queryString); + } + + let statusCode = params.statusCode ? parseInt(params.statusCode) : 200; + let statusReason = params.statusReason ? params.statusReason : "OK"; + aResponse.setStatusLine(aRequest.httpVersion, statusCode, statusReason); + aResponse.setHeader("Cache-Control", "no-cache", false); + + // When a mar download is started by the update service it can finish + // downloading before the ui has loaded. By specifying a serviceURL for the + // update patch that points to this file and has a slowDownloadMar param the + // mar will be downloaded asynchronously which will allow the ui to load + // before the download completes. + if (params.slowDownloadMar) { + aResponse.processAsync(); + aResponse.setHeader("Content-Type", "binary/octet-stream"); + aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR); + + // BITS will first make a HEAD request followed by a GET request. + if (aRequest.method == "HEAD") { + aResponse.finish(); + return; + } + + let retries = 0; + gSlowDownloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + gSlowDownloadTimer.initWithCallback( + function (aTimer) { + let continueFile = getTestDataFile(CONTINUE_DOWNLOAD); + retries++; + if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) { + try { + // If the continue file is in use try again the next time the timer + // fires unless the retries has reached the value defined by + // MAX_SLOW_RESPONSE_RETRIES in which case let the test remove the + // continue file. + if (retries < MAX_SLOW_RESPONSE_RETRIES) { + continueFile.remove(false); + } + gSlowDownloadTimer.cancel(); + aResponse.write(readFileBytes(getTestDataFile(FILE_SIMPLE_MAR))); + aResponse.finish(); + } catch (e) {} + } + }, + SLOW_RESPONSE_INTERVAL, + Ci.nsITimer.TYPE_REPEATING_SLACK + ); + return; + } + + if (params.uiURL) { + aResponse.write( + '<html><head><meta http-equiv="content-type" content=' + + '"text/html; charset=utf-8"></head><body>' + + params.uiURL + + "<br><br>this is a test mar that will not " + + "affect your build.</body></html>" + ); + return; + } + + if (params.xmlMalformed) { + respond(aResponse, params, "xml error"); + return; + } + + if (params.noUpdates) { + respond(aResponse, params, getRemoteUpdatesXMLString("")); + return; + } + + if (params.unsupported) { + let detailsURL = params.detailsURL ? params.detailsURL : URL_HOST; + let unsupportedXML = getRemoteUpdatesXMLString( + ' <update type="major" ' + + 'unsupported="true" ' + + 'detailsURL="' + + detailsURL + + '"></update>\n' + ); + respond(aResponse, params, unsupportedXML); + return; + } + + let size; + let patches = ""; + let url = ""; + if (params.useSlowDownloadMar) { + url = URL_HTTP_UPDATE_SJS + "?slowDownloadMar=1"; + } else { + url = params.badURL ? BAD_SERVICE_URL : SERVICE_URL; + } + if (!params.partialPatchOnly) { + size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : ""); + let patchProps = { type: "complete", url, size }; + patches += getRemotePatchString(patchProps); + } + + if (!params.completePatchOnly) { + size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : ""); + let patchProps = { type: "partial", url, size }; + patches += getRemotePatchString(patchProps); + } + + let updateProps = {}; + if (params.type) { + updateProps.type = params.type; + } + + if (params.name) { + updateProps.name = params.name; + } + + if (params.appVersion) { + updateProps.appVersion = params.appVersion; + } + + if (params.displayVersion) { + updateProps.displayVersion = params.displayVersion; + } + + if (params.buildID) { + updateProps.buildID = params.buildID; + } + + if (params.promptWaitTime) { + updateProps.promptWaitTime = params.promptWaitTime; + } + + if (params.disableBITS) { + updateProps.disableBITS = params.disableBITS; + } + + let updates = getRemoteUpdateString(updateProps, patches); + let xml = getRemoteUpdatesXMLString(updates); + respond(aResponse, params, xml); +} + +function respond(aResponse, aParams, aResponseString) { + if (aParams.slowUpdateCheck) { + let retries = 0; + aResponse.processAsync(); + gSlowCheckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + gSlowCheckTimer.initWithCallback( + function (aTimer) { + retries++; + let continueFile = getTestDataFile(CONTINUE_CHECK); + if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) { + try { + // If the continue file is in use try again the next time the timer + // fires unless the retries has reached the value defined by + // MAX_SLOW_RESPONSE_RETRIES in which case let the test remove the + // continue file. + if (retries < MAX_SLOW_RESPONSE_RETRIES) { + continueFile.remove(false); + } + gSlowCheckTimer.cancel(); + aResponse.write(aResponseString); + aResponse.finish(); + } catch (e) {} + } + }, + SLOW_RESPONSE_INTERVAL, + Ci.nsITimer.TYPE_REPEATING_SLACK + ); + } else { + aResponse.write(aResponseString); + } +} + +/** + * Helper function to create a JS object representing the url parameters from + * the request's queryString. + * + * @param aQueryString + * The request's query string. + * @return A JS object representing the url parameters from the request's + * queryString. + */ +function parseQueryString(aQueryString) { + let paramArray = aQueryString.split("&"); + let regex = /^([^=]+)=(.*)$/; + let params = {}; + for (let i = 0, sz = paramArray.length; i < sz; i++) { + let match = regex.exec(paramArray[i]); + if (!match) { + throw Components.Exception( + "Bad parameter in queryString! '" + paramArray[i] + "'", + Cr.NS_ERROR_ILLEGAL_VALUE + ); + } + params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); + } + + return params; +} diff --git a/toolkit/mozapps/update/tests/data/complete.mar b/toolkit/mozapps/update/tests/data/complete.mar Binary files differnew file mode 100644 index 0000000000..375fd7bd08 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete.mar diff --git a/toolkit/mozapps/update/tests/data/complete.png b/toolkit/mozapps/update/tests/data/complete.png Binary files differnew file mode 100644 index 0000000000..2990a539ff --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete.png diff --git a/toolkit/mozapps/update/tests/data/complete_log_success_mac b/toolkit/mozapps/update/tests/data/complete_log_success_mac new file mode 100644 index 0000000000..4f992a1374 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_log_success_mac @@ -0,0 +1,332 @@ +UPDATE TYPE complete +PREPARE REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0 +PREPARE REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png +PREPARE REMOVEFILE Contents/Resources/removed-files +PREPARE REMOVEFILE Contents/Resources/precomplete +PREPARE REMOVEFILE Contents/Resources/2/20/20text0 +PREPARE REMOVEFILE Contents/Resources/2/20/20png0.png +PREPARE REMOVEFILE Contents/Resources/0/0exe0.exe +PREPARE REMOVEFILE Contents/Resources/0/00/00text0 +PREPARE REMOVEFILE Contents/MacOS/exe0.exe +PREPARE REMOVEDIR Contents/Resources/searchplugins/ +PREPARE REMOVEDIR Contents/Resources/defaults/pref/ +PREPARE REMOVEDIR Contents/Resources/defaults/ +PREPARE REMOVEDIR Contents/Resources/2/20/ +PREPARE REMOVEDIR Contents/Resources/2/ +PREPARE REMOVEDIR Contents/Resources/0/00/ +PREPARE REMOVEDIR Contents/Resources/0/ +PREPARE REMOVEDIR Contents/Resources/ +PREPARE REMOVEDIR Contents/MacOS/ +PREPARE REMOVEDIR Contents/ +PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0 +PREPARE ADD Contents/Resources/searchplugins/searchpluginspng1.png +PREPARE ADD Contents/Resources/searchplugins/searchpluginspng0.png +PREPARE ADD Contents/Resources/removed-files +PREPARE ADD Contents/Resources/precomplete +PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +PREPARE ADD Contents/Resources/1/10/10text0 +PREPARE ADD Contents/Resources/0/0exe0.exe +PREPARE ADD Contents/Resources/0/00/00text1 +PREPARE ADD Contents/Resources/0/00/00text0 +PREPARE ADD Contents/Resources/0/00/00png0.png +PREPARE ADD Contents/MacOS/exe0.exe +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/98/ +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/970/ +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/971/ +PREPARE REMOVEDIR Contents/Resources/9/97/ +PREPARE REMOVEFILE Contents/Resources/9/96/96text0 +PREPARE REMOVEFILE Contents/Resources/9/96/96text1 +PREPARE REMOVEDIR Contents/Resources/9/96/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/93/ +PREPARE REMOVEDIR Contents/Resources/9/92/ +PREPARE REMOVEDIR Contents/Resources/9/91/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/88/ +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/870/ +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/871/ +PREPARE REMOVEDIR Contents/Resources/8/87/ +PREPARE REMOVEFILE Contents/Resources/8/86/86text0 +PREPARE REMOVEFILE Contents/Resources/8/86/86text1 +PREPARE REMOVEDIR Contents/Resources/8/86/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/83/ +PREPARE REMOVEDIR Contents/Resources/8/82/ +PREPARE REMOVEDIR Contents/Resources/8/81/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/70/ +PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/71/ +PREPARE REMOVEFILE Contents/Resources/7/7text0 +PREPARE REMOVEFILE Contents/Resources/7/7text1 +PREPARE REMOVEDIR Contents/Resources/7/ +PREPARE REMOVEDIR Contents/Resources/6/ +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5test.exe +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEDIR Contents/Resources/5/ +PREPARE REMOVEFILE Contents/Resources/4/4text1 +PREPARE REMOVEFILE Contents/Resources/4/4text0 +PREPARE REMOVEDIR Contents/Resources/4/ +PREPARE REMOVEFILE Contents/Resources/3/3text1 +PREPARE REMOVEFILE Contents/Resources/3/3text0 +EXECUTE REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0 +EXECUTE REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png +EXECUTE REMOVEFILE Contents/Resources/removed-files +EXECUTE REMOVEFILE Contents/Resources/precomplete +EXECUTE REMOVEFILE Contents/Resources/2/20/20text0 +EXECUTE REMOVEFILE Contents/Resources/2/20/20png0.png +EXECUTE REMOVEFILE Contents/Resources/0/0exe0.exe +EXECUTE REMOVEFILE Contents/Resources/0/00/00text0 +EXECUTE REMOVEFILE Contents/MacOS/exe0.exe +EXECUTE REMOVEDIR Contents/Resources/searchplugins/ +EXECUTE REMOVEDIR Contents/Resources/defaults/pref/ +EXECUTE REMOVEDIR Contents/Resources/defaults/ +EXECUTE REMOVEDIR Contents/Resources/2/20/ +EXECUTE REMOVEDIR Contents/Resources/2/ +EXECUTE REMOVEDIR Contents/Resources/0/00/ +EXECUTE REMOVEDIR Contents/Resources/0/ +EXECUTE REMOVEDIR Contents/Resources/ +EXECUTE REMOVEDIR Contents/MacOS/ +EXECUTE REMOVEDIR Contents/ +EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0 +EXECUTE ADD Contents/Resources/searchplugins/searchpluginspng1.png +EXECUTE ADD Contents/Resources/searchplugins/searchpluginspng0.png +EXECUTE ADD Contents/Resources/removed-files +EXECUTE ADD Contents/Resources/precomplete +EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +EXECUTE ADD Contents/Resources/1/10/10text0 +EXECUTE ADD Contents/Resources/0/0exe0.exe +EXECUTE ADD Contents/Resources/0/00/00text1 +EXECUTE ADD Contents/Resources/0/00/00text0 +EXECUTE ADD Contents/Resources/0/00/00png0.png +EXECUTE ADD Contents/MacOS/exe0.exe +EXECUTE REMOVEDIR Contents/Resources/9/99/ +EXECUTE REMOVEDIR Contents/Resources/9/99/ +EXECUTE REMOVEDIR Contents/Resources/9/98/ +EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext0 +EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext1 +EXECUTE REMOVEDIR Contents/Resources/9/97/970/ +EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext0 +EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext1 +EXECUTE REMOVEDIR Contents/Resources/9/97/971/ +EXECUTE REMOVEDIR Contents/Resources/9/97/ +EXECUTE REMOVEFILE Contents/Resources/9/96/96text0 +EXECUTE REMOVEFILE Contents/Resources/9/96/96text1 +EXECUTE REMOVEDIR Contents/Resources/9/96/ +EXECUTE REMOVEDIR Contents/Resources/9/95/ +EXECUTE REMOVEDIR Contents/Resources/9/95/ +EXECUTE REMOVEDIR Contents/Resources/9/94/ +EXECUTE REMOVEDIR Contents/Resources/9/94/ +EXECUTE REMOVEDIR Contents/Resources/9/93/ +EXECUTE REMOVEDIR Contents/Resources/9/92/ +EXECUTE REMOVEDIR Contents/Resources/9/91/ +EXECUTE REMOVEDIR Contents/Resources/9/90/ +EXECUTE REMOVEDIR Contents/Resources/9/90/ +EXECUTE REMOVEDIR Contents/Resources/8/89/ +EXECUTE REMOVEDIR Contents/Resources/8/89/ +EXECUTE REMOVEDIR Contents/Resources/8/88/ +EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext0 +EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext1 +EXECUTE REMOVEDIR Contents/Resources/8/87/870/ +EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext0 +EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext1 +EXECUTE REMOVEDIR Contents/Resources/8/87/871/ +EXECUTE REMOVEDIR Contents/Resources/8/87/ +EXECUTE REMOVEFILE Contents/Resources/8/86/86text0 +EXECUTE REMOVEFILE Contents/Resources/8/86/86text1 +EXECUTE REMOVEDIR Contents/Resources/8/86/ +EXECUTE REMOVEDIR Contents/Resources/8/85/ +EXECUTE REMOVEDIR Contents/Resources/8/85/ +EXECUTE REMOVEDIR Contents/Resources/8/84/ +EXECUTE REMOVEDIR Contents/Resources/8/84/ +EXECUTE REMOVEDIR Contents/Resources/8/83/ +EXECUTE REMOVEDIR Contents/Resources/8/82/ +EXECUTE REMOVEDIR Contents/Resources/8/81/ +EXECUTE REMOVEDIR Contents/Resources/8/80/ +EXECUTE REMOVEDIR Contents/Resources/8/80/ +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtest.exe +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext0 +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext1 +EXECUTE REMOVEDIR Contents/Resources/7/70/ +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtest.exe +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext0 +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext1 +EXECUTE REMOVEDIR Contents/Resources/7/71/ +EXECUTE REMOVEFILE Contents/Resources/7/7text0 +EXECUTE REMOVEFILE Contents/Resources/7/7text1 +EXECUTE REMOVEDIR Contents/Resources/7/ +EXECUTE REMOVEDIR Contents/Resources/6/ +EXECUTE REMOVEFILE Contents/Resources/5/5text1 +EXECUTE REMOVEFILE Contents/Resources/5/5text0 +EXECUTE REMOVEFILE Contents/Resources/5/5test.exe +EXECUTE REMOVEFILE Contents/Resources/5/5text0 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEFILE Contents/Resources/5/5text1 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEDIR Contents/Resources/5/ +EXECUTE REMOVEFILE Contents/Resources/4/4text1 +EXECUTE REMOVEFILE Contents/Resources/4/4text0 +EXECUTE REMOVEDIR Contents/Resources/4/ +EXECUTE REMOVEFILE Contents/Resources/3/3text1 +EXECUTE REMOVEFILE Contents/Resources/3/3text0 +FINISH REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0 +FINISH REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png +FINISH REMOVEFILE Contents/Resources/removed-files +FINISH REMOVEFILE Contents/Resources/precomplete +FINISH REMOVEFILE Contents/Resources/2/20/20text0 +FINISH REMOVEFILE Contents/Resources/2/20/20png0.png +FINISH REMOVEFILE Contents/Resources/0/0exe0.exe +FINISH REMOVEFILE Contents/Resources/0/00/00text0 +FINISH REMOVEFILE Contents/MacOS/exe0.exe +FINISH REMOVEDIR Contents/Resources/searchplugins/ +removing directory: Contents/Resources/searchplugins/, rv: 0 +FINISH REMOVEDIR Contents/Resources/defaults/pref/ +removing directory: Contents/Resources/defaults/pref/, rv: 0 +FINISH REMOVEDIR Contents/Resources/defaults/ +removing directory: Contents/Resources/defaults/, rv: 0 +FINISH REMOVEDIR Contents/Resources/2/20/ +FINISH REMOVEDIR Contents/Resources/2/ +FINISH REMOVEDIR Contents/Resources/0/00/ +removing directory: Contents/Resources/0/00/, rv: 0 +FINISH REMOVEDIR Contents/Resources/0/ +removing directory: Contents/Resources/0/, rv: 0 +FINISH REMOVEDIR Contents/Resources/ +removing directory: Contents/Resources/, rv: 0 +FINISH REMOVEDIR Contents/MacOS/ +removing directory: Contents/MacOS/, rv: 0 +FINISH REMOVEDIR Contents/ +removing directory: Contents/, rv: 0 +FINISH ADD Contents/Resources/searchplugins/searchpluginstext0 +FINISH ADD Contents/Resources/searchplugins/searchpluginspng1.png +FINISH ADD Contents/Resources/searchplugins/searchpluginspng0.png +FINISH ADD Contents/Resources/removed-files +FINISH ADD Contents/Resources/precomplete +FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +FINISH ADD Contents/Resources/1/10/10text0 +FINISH ADD Contents/Resources/0/0exe0.exe +FINISH ADD Contents/Resources/0/00/00text1 +FINISH ADD Contents/Resources/0/00/00text0 +FINISH ADD Contents/Resources/0/00/00png0.png +FINISH ADD Contents/MacOS/exe0.exe +FINISH REMOVEDIR Contents/Resources/9/99/ +FINISH REMOVEDIR Contents/Resources/9/99/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/98/ +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0 +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1 +FINISH REMOVEDIR Contents/Resources/9/97/970/ +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0 +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1 +FINISH REMOVEDIR Contents/Resources/9/97/971/ +FINISH REMOVEDIR Contents/Resources/9/97/ +FINISH REMOVEFILE Contents/Resources/9/96/96text0 +FINISH REMOVEFILE Contents/Resources/9/96/96text1 +FINISH REMOVEDIR Contents/Resources/9/96/ +FINISH REMOVEDIR Contents/Resources/9/95/ +FINISH REMOVEDIR Contents/Resources/9/95/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/94/ +FINISH REMOVEDIR Contents/Resources/9/94/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/93/ +FINISH REMOVEDIR Contents/Resources/9/92/ +removing directory: Contents/Resources/9/92/, rv: 0 +FINISH REMOVEDIR Contents/Resources/9/91/ +removing directory: Contents/Resources/9/91/, rv: 0 +FINISH REMOVEDIR Contents/Resources/9/90/ +FINISH REMOVEDIR Contents/Resources/9/90/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/89/ +FINISH REMOVEDIR Contents/Resources/8/89/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/88/ +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0 +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1 +FINISH REMOVEDIR Contents/Resources/8/87/870/ +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0 +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1 +FINISH REMOVEDIR Contents/Resources/8/87/871/ +FINISH REMOVEDIR Contents/Resources/8/87/ +FINISH REMOVEFILE Contents/Resources/8/86/86text0 +FINISH REMOVEFILE Contents/Resources/8/86/86text1 +FINISH REMOVEDIR Contents/Resources/8/86/ +FINISH REMOVEDIR Contents/Resources/8/85/ +FINISH REMOVEDIR Contents/Resources/8/85/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/84/ +FINISH REMOVEDIR Contents/Resources/8/84/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/83/ +FINISH REMOVEDIR Contents/Resources/8/82/ +removing directory: Contents/Resources/8/82/, rv: 0 +FINISH REMOVEDIR Contents/Resources/8/81/ +removing directory: Contents/Resources/8/81/, rv: 0 +FINISH REMOVEDIR Contents/Resources/8/80/ +FINISH REMOVEDIR Contents/Resources/8/80/ +directory no longer exists; skipping +FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe +FINISH REMOVEFILE Contents/Resources/7/70/7xtext0 +FINISH REMOVEFILE Contents/Resources/7/70/7xtext1 +FINISH REMOVEDIR Contents/Resources/7/70/ +FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe +FINISH REMOVEFILE Contents/Resources/7/71/7xtext0 +FINISH REMOVEFILE Contents/Resources/7/71/7xtext1 +FINISH REMOVEDIR Contents/Resources/7/71/ +FINISH REMOVEFILE Contents/Resources/7/7text0 +FINISH REMOVEFILE Contents/Resources/7/7text1 +FINISH REMOVEDIR Contents/Resources/7/ +FINISH REMOVEDIR Contents/Resources/6/ +FINISH REMOVEFILE Contents/Resources/5/5text1 +FINISH REMOVEFILE Contents/Resources/5/5text0 +FINISH REMOVEFILE Contents/Resources/5/5test.exe +FINISH REMOVEDIR Contents/Resources/5/ +FINISH REMOVEFILE Contents/Resources/4/4text1 +FINISH REMOVEFILE Contents/Resources/4/4text0 +FINISH REMOVEDIR Contents/Resources/4/ +FINISH REMOVEFILE Contents/Resources/3/3text1 +FINISH REMOVEFILE Contents/Resources/3/3text0 +succeeded +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/complete_log_success_win b/toolkit/mozapps/update/tests/data/complete_log_success_win new file mode 100644 index 0000000000..c5a03dc9d6 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_log_success_win @@ -0,0 +1,320 @@ +UPDATE TYPE complete +PREPARE REMOVEFILE searchplugins/searchpluginstext0 +PREPARE REMOVEFILE searchplugins/searchpluginspng0.png +PREPARE REMOVEFILE removed-files +PREPARE REMOVEFILE precomplete +PREPARE REMOVEFILE exe0.exe +PREPARE REMOVEFILE 2/20/20text0 +PREPARE REMOVEFILE 2/20/20png0.png +PREPARE REMOVEFILE 0/0exe0.exe +PREPARE REMOVEFILE 0/00/00text0 +PREPARE REMOVEDIR searchplugins/ +PREPARE REMOVEDIR defaults/pref/ +PREPARE REMOVEDIR defaults/ +PREPARE REMOVEDIR 2/20/ +PREPARE REMOVEDIR 2/ +PREPARE REMOVEDIR 0/00/ +PREPARE REMOVEDIR 0/ +PREPARE ADD searchplugins/searchpluginstext0 +PREPARE ADD searchplugins/searchpluginspng1.png +PREPARE ADD searchplugins/searchpluginspng0.png +PREPARE ADD removed-files +PREPARE ADD precomplete +PREPARE ADD exe0.exe +PREPARE ADD distribution/extensions/extensions1/extensions1text0 +PREPARE ADD distribution/extensions/extensions1/extensions1png1.png +PREPARE ADD distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD distribution/extensions/extensions0/extensions0text0 +PREPARE ADD distribution/extensions/extensions0/extensions0png1.png +PREPARE ADD distribution/extensions/extensions0/extensions0png0.png +PREPARE ADD 1/10/10text0 +PREPARE ADD 0/0exe0.exe +PREPARE ADD 0/00/00text1 +PREPARE ADD 0/00/00text0 +PREPARE ADD 0/00/00png0.png +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/98/ +PREPARE REMOVEFILE 9/97/970/97xtext0 +PREPARE REMOVEFILE 9/97/970/97xtext1 +PREPARE REMOVEDIR 9/97/970/ +PREPARE REMOVEFILE 9/97/971/97xtext0 +PREPARE REMOVEFILE 9/97/971/97xtext1 +PREPARE REMOVEDIR 9/97/971/ +PREPARE REMOVEDIR 9/97/ +PREPARE REMOVEFILE 9/96/96text0 +PREPARE REMOVEFILE 9/96/96text1 +PREPARE REMOVEDIR 9/96/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/93/ +PREPARE REMOVEDIR 9/92/ +PREPARE REMOVEDIR 9/91/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/88/ +PREPARE REMOVEFILE 8/87/870/87xtext0 +PREPARE REMOVEFILE 8/87/870/87xtext1 +PREPARE REMOVEDIR 8/87/870/ +PREPARE REMOVEFILE 8/87/871/87xtext0 +PREPARE REMOVEFILE 8/87/871/87xtext1 +PREPARE REMOVEDIR 8/87/871/ +PREPARE REMOVEDIR 8/87/ +PREPARE REMOVEFILE 8/86/86text0 +PREPARE REMOVEFILE 8/86/86text1 +PREPARE REMOVEDIR 8/86/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/83/ +PREPARE REMOVEDIR 8/82/ +PREPARE REMOVEDIR 8/81/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEFILE 7/70/7xtest.exe +PREPARE REMOVEFILE 7/70/7xtext0 +PREPARE REMOVEFILE 7/70/7xtext1 +PREPARE REMOVEDIR 7/70/ +PREPARE REMOVEFILE 7/71/7xtest.exe +PREPARE REMOVEFILE 7/71/7xtext0 +PREPARE REMOVEFILE 7/71/7xtext1 +PREPARE REMOVEDIR 7/71/ +PREPARE REMOVEFILE 7/7text0 +PREPARE REMOVEFILE 7/7text1 +PREPARE REMOVEDIR 7/ +PREPARE REMOVEDIR 6/ +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5test.exe +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEDIR 5/ +PREPARE REMOVEFILE 4/4text1 +PREPARE REMOVEFILE 4/4text0 +PREPARE REMOVEDIR 4/ +PREPARE REMOVEFILE 3/3text1 +PREPARE REMOVEFILE 3/3text0 +EXECUTE REMOVEFILE searchplugins/searchpluginstext0 +EXECUTE REMOVEFILE searchplugins/searchpluginspng0.png +EXECUTE REMOVEFILE removed-files +EXECUTE REMOVEFILE precomplete +EXECUTE REMOVEFILE exe0.exe +EXECUTE REMOVEFILE 2/20/20text0 +EXECUTE REMOVEFILE 2/20/20png0.png +EXECUTE REMOVEFILE 0/0exe0.exe +EXECUTE REMOVEFILE 0/00/00text0 +EXECUTE REMOVEDIR searchplugins/ +EXECUTE REMOVEDIR defaults/pref/ +EXECUTE REMOVEDIR defaults/ +EXECUTE REMOVEDIR 2/20/ +EXECUTE REMOVEDIR 2/ +EXECUTE REMOVEDIR 0/00/ +EXECUTE REMOVEDIR 0/ +EXECUTE ADD searchplugins/searchpluginstext0 +EXECUTE ADD searchplugins/searchpluginspng1.png +EXECUTE ADD searchplugins/searchpluginspng0.png +EXECUTE ADD removed-files +EXECUTE ADD precomplete +EXECUTE ADD exe0.exe +EXECUTE ADD distribution/extensions/extensions1/extensions1text0 +EXECUTE ADD distribution/extensions/extensions1/extensions1png1.png +EXECUTE ADD distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD distribution/extensions/extensions0/extensions0text0 +EXECUTE ADD distribution/extensions/extensions0/extensions0png1.png +EXECUTE ADD distribution/extensions/extensions0/extensions0png0.png +EXECUTE ADD 1/10/10text0 +EXECUTE ADD 0/0exe0.exe +EXECUTE ADD 0/00/00text1 +EXECUTE ADD 0/00/00text0 +EXECUTE ADD 0/00/00png0.png +EXECUTE REMOVEDIR 9/99/ +EXECUTE REMOVEDIR 9/99/ +EXECUTE REMOVEDIR 9/98/ +EXECUTE REMOVEFILE 9/97/970/97xtext0 +EXECUTE REMOVEFILE 9/97/970/97xtext1 +EXECUTE REMOVEDIR 9/97/970/ +EXECUTE REMOVEFILE 9/97/971/97xtext0 +EXECUTE REMOVEFILE 9/97/971/97xtext1 +EXECUTE REMOVEDIR 9/97/971/ +EXECUTE REMOVEDIR 9/97/ +EXECUTE REMOVEFILE 9/96/96text0 +EXECUTE REMOVEFILE 9/96/96text1 +EXECUTE REMOVEDIR 9/96/ +EXECUTE REMOVEDIR 9/95/ +EXECUTE REMOVEDIR 9/95/ +EXECUTE REMOVEDIR 9/94/ +EXECUTE REMOVEDIR 9/94/ +EXECUTE REMOVEDIR 9/93/ +EXECUTE REMOVEDIR 9/92/ +EXECUTE REMOVEDIR 9/91/ +EXECUTE REMOVEDIR 9/90/ +EXECUTE REMOVEDIR 9/90/ +EXECUTE REMOVEDIR 8/89/ +EXECUTE REMOVEDIR 8/89/ +EXECUTE REMOVEDIR 8/88/ +EXECUTE REMOVEFILE 8/87/870/87xtext0 +EXECUTE REMOVEFILE 8/87/870/87xtext1 +EXECUTE REMOVEDIR 8/87/870/ +EXECUTE REMOVEFILE 8/87/871/87xtext0 +EXECUTE REMOVEFILE 8/87/871/87xtext1 +EXECUTE REMOVEDIR 8/87/871/ +EXECUTE REMOVEDIR 8/87/ +EXECUTE REMOVEFILE 8/86/86text0 +EXECUTE REMOVEFILE 8/86/86text1 +EXECUTE REMOVEDIR 8/86/ +EXECUTE REMOVEDIR 8/85/ +EXECUTE REMOVEDIR 8/85/ +EXECUTE REMOVEDIR 8/84/ +EXECUTE REMOVEDIR 8/84/ +EXECUTE REMOVEDIR 8/83/ +EXECUTE REMOVEDIR 8/82/ +EXECUTE REMOVEDIR 8/81/ +EXECUTE REMOVEDIR 8/80/ +EXECUTE REMOVEDIR 8/80/ +EXECUTE REMOVEFILE 7/70/7xtest.exe +EXECUTE REMOVEFILE 7/70/7xtext0 +EXECUTE REMOVEFILE 7/70/7xtext1 +EXECUTE REMOVEDIR 7/70/ +EXECUTE REMOVEFILE 7/71/7xtest.exe +EXECUTE REMOVEFILE 7/71/7xtext0 +EXECUTE REMOVEFILE 7/71/7xtext1 +EXECUTE REMOVEDIR 7/71/ +EXECUTE REMOVEFILE 7/7text0 +EXECUTE REMOVEFILE 7/7text1 +EXECUTE REMOVEDIR 7/ +EXECUTE REMOVEDIR 6/ +EXECUTE REMOVEFILE 5/5text1 +EXECUTE REMOVEFILE 5/5text0 +EXECUTE REMOVEFILE 5/5test.exe +EXECUTE REMOVEFILE 5/5text0 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEFILE 5/5text1 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEDIR 5/ +EXECUTE REMOVEFILE 4/4text1 +EXECUTE REMOVEFILE 4/4text0 +EXECUTE REMOVEDIR 4/ +EXECUTE REMOVEFILE 3/3text1 +EXECUTE REMOVEFILE 3/3text0 +FINISH REMOVEFILE searchplugins/searchpluginstext0 +FINISH REMOVEFILE searchplugins/searchpluginspng0.png +FINISH REMOVEFILE removed-files +FINISH REMOVEFILE precomplete +FINISH REMOVEFILE exe0.exe +FINISH REMOVEFILE 2/20/20text0 +FINISH REMOVEFILE 2/20/20png0.png +FINISH REMOVEFILE 0/0exe0.exe +FINISH REMOVEFILE 0/00/00text0 +FINISH REMOVEDIR searchplugins/ +removing directory: searchplugins/, rv: 0 +FINISH REMOVEDIR defaults/pref/ +removing directory: defaults/pref/, rv: 0 +FINISH REMOVEDIR defaults/ +removing directory: defaults/, rv: 0 +FINISH REMOVEDIR 2/20/ +FINISH REMOVEDIR 2/ +FINISH REMOVEDIR 0/00/ +removing directory: 0/00/, rv: 0 +FINISH REMOVEDIR 0/ +removing directory: 0/, rv: 0 +FINISH ADD searchplugins/searchpluginstext0 +FINISH ADD searchplugins/searchpluginspng1.png +FINISH ADD searchplugins/searchpluginspng0.png +FINISH ADD removed-files +FINISH ADD precomplete +FINISH ADD exe0.exe +FINISH ADD distribution/extensions/extensions1/extensions1text0 +FINISH ADD distribution/extensions/extensions1/extensions1png1.png +FINISH ADD distribution/extensions/extensions1/extensions1png0.png +FINISH ADD distribution/extensions/extensions0/extensions0text0 +FINISH ADD distribution/extensions/extensions0/extensions0png1.png +FINISH ADD distribution/extensions/extensions0/extensions0png0.png +FINISH ADD 1/10/10text0 +FINISH ADD 0/0exe0.exe +FINISH ADD 0/00/00text1 +FINISH ADD 0/00/00text0 +FINISH ADD 0/00/00png0.png +FINISH REMOVEDIR 9/99/ +FINISH REMOVEDIR 9/99/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/98/ +FINISH REMOVEFILE 9/97/970/97xtext0 +FINISH REMOVEFILE 9/97/970/97xtext1 +FINISH REMOVEDIR 9/97/970/ +FINISH REMOVEFILE 9/97/971/97xtext0 +FINISH REMOVEFILE 9/97/971/97xtext1 +FINISH REMOVEDIR 9/97/971/ +FINISH REMOVEDIR 9/97/ +FINISH REMOVEFILE 9/96/96text0 +FINISH REMOVEFILE 9/96/96text1 +FINISH REMOVEDIR 9/96/ +FINISH REMOVEDIR 9/95/ +FINISH REMOVEDIR 9/95/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/94/ +FINISH REMOVEDIR 9/94/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/93/ +FINISH REMOVEDIR 9/92/ +removing directory: 9/92/, rv: 0 +FINISH REMOVEDIR 9/91/ +removing directory: 9/91/, rv: 0 +FINISH REMOVEDIR 9/90/ +FINISH REMOVEDIR 9/90/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/89/ +FINISH REMOVEDIR 8/89/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/88/ +FINISH REMOVEFILE 8/87/870/87xtext0 +FINISH REMOVEFILE 8/87/870/87xtext1 +FINISH REMOVEDIR 8/87/870/ +FINISH REMOVEFILE 8/87/871/87xtext0 +FINISH REMOVEFILE 8/87/871/87xtext1 +FINISH REMOVEDIR 8/87/871/ +FINISH REMOVEDIR 8/87/ +FINISH REMOVEFILE 8/86/86text0 +FINISH REMOVEFILE 8/86/86text1 +FINISH REMOVEDIR 8/86/ +FINISH REMOVEDIR 8/85/ +FINISH REMOVEDIR 8/85/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/84/ +FINISH REMOVEDIR 8/84/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/83/ +FINISH REMOVEDIR 8/82/ +removing directory: 8/82/, rv: 0 +FINISH REMOVEDIR 8/81/ +removing directory: 8/81/, rv: 0 +FINISH REMOVEDIR 8/80/ +FINISH REMOVEDIR 8/80/ +directory no longer exists; skipping +FINISH REMOVEFILE 7/70/7xtest.exe +FINISH REMOVEFILE 7/70/7xtext0 +FINISH REMOVEFILE 7/70/7xtext1 +FINISH REMOVEDIR 7/70/ +FINISH REMOVEFILE 7/71/7xtest.exe +FINISH REMOVEFILE 7/71/7xtext0 +FINISH REMOVEFILE 7/71/7xtext1 +FINISH REMOVEDIR 7/71/ +FINISH REMOVEFILE 7/7text0 +FINISH REMOVEFILE 7/7text1 +FINISH REMOVEDIR 7/ +FINISH REMOVEDIR 6/ +FINISH REMOVEFILE 5/5text1 +FINISH REMOVEFILE 5/5text0 +FINISH REMOVEFILE 5/5test.exe +FINISH REMOVEDIR 5/ +FINISH REMOVEFILE 4/4text1 +FINISH REMOVEFILE 4/4text0 +FINISH REMOVEDIR 4/ +FINISH REMOVEFILE 3/3text1 +FINISH REMOVEFILE 3/3text0 +succeeded +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/complete_mac.mar b/toolkit/mozapps/update/tests/data/complete_mac.mar Binary files differnew file mode 100644 index 0000000000..c54088610a --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_mac.mar diff --git a/toolkit/mozapps/update/tests/data/complete_precomplete b/toolkit/mozapps/update/tests/data/complete_precomplete new file mode 100644 index 0000000000..ae7a0013ff --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_precomplete @@ -0,0 +1,18 @@ +remove "searchplugins/searchpluginstext0" +remove "searchplugins/searchpluginspng1.png" +remove "searchplugins/searchpluginspng0.png" +remove "removed-files" +remove "precomplete" +remove "exe0.exe" +remove "1/10/10text0" +remove "0/0exe0.exe" +remove "0/00/00text1" +remove "0/00/00text0" +remove "0/00/00png0.png" +rmdir "searchplugins/" +rmdir "defaults/pref/" +rmdir "defaults/" +rmdir "1/10/" +rmdir "1/" +rmdir "0/00/" +rmdir "0/" diff --git a/toolkit/mozapps/update/tests/data/complete_precomplete_mac b/toolkit/mozapps/update/tests/data/complete_precomplete_mac new file mode 100644 index 0000000000..8d81a36d66 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_precomplete_mac @@ -0,0 +1,21 @@ +remove "Contents/Resources/searchplugins/searchpluginstext0" +remove "Contents/Resources/searchplugins/searchpluginspng1.png" +remove "Contents/Resources/searchplugins/searchpluginspng0.png" +remove "Contents/Resources/removed-files" +remove "Contents/Resources/precomplete" +remove "Contents/Resources/1/10/10text0" +remove "Contents/Resources/0/0exe0.exe" +remove "Contents/Resources/0/00/00text1" +remove "Contents/Resources/0/00/00text0" +remove "Contents/Resources/0/00/00png0.png" +remove "Contents/MacOS/exe0.exe" +rmdir "Contents/Resources/searchplugins/" +rmdir "Contents/Resources/defaults/pref/" +rmdir "Contents/Resources/defaults/" +rmdir "Contents/Resources/1/10/" +rmdir "Contents/Resources/1/" +rmdir "Contents/Resources/0/00/" +rmdir "Contents/Resources/0/" +rmdir "Contents/Resources/" +rmdir "Contents/MacOS/" +rmdir "Contents/" diff --git a/toolkit/mozapps/update/tests/data/complete_removed-files b/toolkit/mozapps/update/tests/data/complete_removed-files new file mode 100644 index 0000000000..e45c43c1f8 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_removed-files @@ -0,0 +1,41 @@ +text0 +text1 +3/3text0 +3/3text1 +4/exe0.exe +4/4text0 +4/4text1 +4/ +5/5text0 +5/5text1 +5/* +6/ +7/* +8/80/ +8/81/ +8/82/ +8/83/ +8/84/ +8/85/* +8/86/* +8/87/* +8/88/* +8/89/* +8/80/ +8/84/* +8/85/* +8/89/ +9/90/ +9/91/ +9/92/ +9/93/ +9/94/ +9/95/* +9/96/* +9/97/* +9/98/* +9/99/* +9/90/ +9/94/* +9/95/* +9/99/ diff --git a/toolkit/mozapps/update/tests/data/complete_removed-files_mac b/toolkit/mozapps/update/tests/data/complete_removed-files_mac new file mode 100644 index 0000000000..955dc5b340 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_removed-files_mac @@ -0,0 +1,41 @@ +Contents/Resources/text0 +Contents/Resources/text1 +Contents/Resources/3/3text0 +Contents/Resources/3/3text1 +Contents/Resources/4/exe0.exe +Contents/Resources/4/4text0 +Contents/Resources/4/4text1 +Contents/Resources/4/ +Contents/Resources/5/5text0 +Contents/Resources/5/5text1 +Contents/Resources/5/* +Contents/Resources/6/ +Contents/Resources/7/* +Contents/Resources/8/80/ +Contents/Resources/8/81/ +Contents/Resources/8/82/ +Contents/Resources/8/83/ +Contents/Resources/8/84/ +Contents/Resources/8/85/* +Contents/Resources/8/86/* +Contents/Resources/8/87/* +Contents/Resources/8/88/* +Contents/Resources/8/89/* +Contents/Resources/8/80/ +Contents/Resources/8/84/* +Contents/Resources/8/85/* +Contents/Resources/8/89/ +Contents/Resources/9/90/ +Contents/Resources/9/91/ +Contents/Resources/9/92/ +Contents/Resources/9/93/ +Contents/Resources/9/94/ +Contents/Resources/9/95/* +Contents/Resources/9/96/* +Contents/Resources/9/97/* +Contents/Resources/9/98/* +Contents/Resources/9/99/* +Contents/Resources/9/90/ +Contents/Resources/9/94/* +Contents/Resources/9/95/* +Contents/Resources/9/99/ diff --git a/toolkit/mozapps/update/tests/data/complete_update_manifest b/toolkit/mozapps/update/tests/data/complete_update_manifest new file mode 100644 index 0000000000..383a324f63 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/complete_update_manifest @@ -0,0 +1,59 @@ +type "complete" +add "precomplete" +add "searchplugins/searchpluginstext0" +add "searchplugins/searchpluginspng1.png" +add "searchplugins/searchpluginspng0.png" +add "removed-files" +add-if "extensions/extensions1" "extensions/extensions1/extensions1text0" +add-if "extensions/extensions1" "extensions/extensions1/extensions1png1.png" +add-if "extensions/extensions1" "extensions/extensions1/extensions1png0.png" +add-if "extensions/extensions0" "extensions/extensions0/extensions0text0" +add-if "extensions/extensions0" "extensions/extensions0/extensions0png1.png" +add-if "extensions/extensions0" "extensions/extensions0/extensions0png0.png" +add "exe0.exe" +add "1/10/10text0" +add "0/0exe0.exe" +add "0/00/00text1" +add "0/00/00text0" +add "0/00/00png0.png" +remove "text1" +remove "text0" +rmrfdir "9/99/" +rmdir "9/99/" +rmrfdir "9/98/" +rmrfdir "9/97/" +rmrfdir "9/96/" +rmrfdir "9/95/" +rmrfdir "9/95/" +rmrfdir "9/94/" +rmdir "9/94/" +rmdir "9/93/" +rmdir "9/92/" +rmdir "9/91/" +rmdir "9/90/" +rmdir "9/90/" +rmrfdir "8/89/" +rmdir "8/89/" +rmrfdir "8/88/" +rmrfdir "8/87/" +rmrfdir "8/86/" +rmrfdir "8/85/" +rmrfdir "8/85/" +rmrfdir "8/84/" +rmdir "8/84/" +rmdir "8/83/" +rmdir "8/82/" +rmdir "8/81/" +rmdir "8/80/" +rmdir "8/80/" +rmrfdir "7/" +rmdir "6/" +remove "5/5text1" +remove "5/5text0" +rmrfdir "5/" +remove "4/exe0.exe" +remove "4/4text1" +remove "4/4text0" +rmdir "4/" +remove "3/3text1" +remove "3/3text0" diff --git a/toolkit/mozapps/update/tests/data/old_version.mar b/toolkit/mozapps/update/tests/data/old_version.mar Binary files differnew file mode 100644 index 0000000000..b48f1d5fa4 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/old_version.mar diff --git a/toolkit/mozapps/update/tests/data/partial.mar b/toolkit/mozapps/update/tests/data/partial.mar Binary files differnew file mode 100644 index 0000000000..b6b04bbdbf --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial.mar diff --git a/toolkit/mozapps/update/tests/data/partial.png b/toolkit/mozapps/update/tests/data/partial.png Binary files differnew file mode 100644 index 0000000000..9246f586c7 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial.png diff --git a/toolkit/mozapps/update/tests/data/partial_log_failure_mac b/toolkit/mozapps/update/tests/data/partial_log_failure_mac new file mode 100644 index 0000000000..3b2933ebd2 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_log_failure_mac @@ -0,0 +1,192 @@ +UPDATE TYPE partial +PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0 +PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng1.png +PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng0.png +PREPARE ADD Contents/Resources/precomplete +PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +PREPARE PATCH Contents/Resources/0/0exe0.exe +PREPARE ADD Contents/Resources/0/00/00text0 +PREPARE PATCH Contents/Resources/0/00/00png0.png +PREPARE PATCH Contents/MacOS/exe0.exe +PREPARE ADD Contents/Resources/2/20/20text0 +PREPARE ADD Contents/Resources/2/20/20png0.png +PREPARE ADD Contents/Resources/0/00/00text2 +PREPARE REMOVEFILE Contents/Resources/1/10/10text0 +PREPARE REMOVEFILE Contents/Resources/0/00/00text1 +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/98/ +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/970/ +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/971/ +PREPARE REMOVEDIR Contents/Resources/9/97/ +PREPARE REMOVEFILE Contents/Resources/9/96/96text0 +PREPARE REMOVEFILE Contents/Resources/9/96/96text1 +PREPARE REMOVEDIR Contents/Resources/9/96/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/93/ +PREPARE REMOVEDIR Contents/Resources/9/92/ +PREPARE REMOVEDIR Contents/Resources/9/91/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/88/ +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/870/ +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/871/ +PREPARE REMOVEDIR Contents/Resources/8/87/ +PREPARE REMOVEFILE Contents/Resources/8/86/86text0 +PREPARE REMOVEFILE Contents/Resources/8/86/86text1 +PREPARE REMOVEDIR Contents/Resources/8/86/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/83/ +PREPARE REMOVEDIR Contents/Resources/8/82/ +PREPARE REMOVEDIR Contents/Resources/8/81/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/70/ +PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/71/ +PREPARE REMOVEFILE Contents/Resources/7/7text0 +PREPARE REMOVEFILE Contents/Resources/7/7text1 +PREPARE REMOVEDIR Contents/Resources/7/ +PREPARE REMOVEDIR Contents/Resources/6/ +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5test.exe +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEDIR Contents/Resources/5/ +PREPARE REMOVEFILE Contents/Resources/4/4text1 +PREPARE REMOVEFILE Contents/Resources/4/4text0 +PREPARE REMOVEDIR Contents/Resources/4/ +PREPARE REMOVEFILE Contents/Resources/3/3text1 +PREPARE REMOVEFILE Contents/Resources/3/3text0 +PREPARE REMOVEDIR Contents/Resources/1/10/ +PREPARE REMOVEDIR Contents/Resources/1/ +EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0 +EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng1.png +EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng0.png +EXECUTE ADD Contents/Resources/precomplete +EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +EXECUTE PATCH Contents/Resources/0/0exe0.exe +LoadSourceFile: destination file size 776 does not match expected size 79872 +LoadSourceFile failed +### execution failed +FINISH ADD Contents/Resources/searchplugins/searchpluginstext0 +FINISH PATCH Contents/Resources/searchplugins/searchpluginspng1.png +FINISH PATCH Contents/Resources/searchplugins/searchpluginspng0.png +FINISH ADD Contents/Resources/precomplete +FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +backup_restore: backup file doesn't exist: Contents/Resources/distribution/extensions/extensions1/extensions1text0.moz-backup +FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +FINISH PATCH Contents/Resources/0/0exe0.exe +backup_restore: backup file doesn't exist: Contents/Resources/0/0exe0.exe.moz-backup +FINISH ADD Contents/Resources/0/00/00text0 +backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text0.moz-backup +FINISH PATCH Contents/Resources/0/00/00png0.png +backup_restore: backup file doesn't exist: Contents/Resources/0/00/00png0.png.moz-backup +FINISH PATCH Contents/MacOS/exe0.exe +backup_restore: backup file doesn't exist: Contents/MacOS/exe0.exe.moz-backup +FINISH ADD Contents/Resources/2/20/20text0 +backup_restore: backup file doesn't exist: Contents/Resources/2/20/20text0.moz-backup +FINISH ADD Contents/Resources/2/20/20png0.png +backup_restore: backup file doesn't exist: Contents/Resources/2/20/20png0.png.moz-backup +FINISH ADD Contents/Resources/0/00/00text2 +backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text2.moz-backup +FINISH REMOVEFILE Contents/Resources/1/10/10text0 +backup_restore: backup file doesn't exist: Contents/Resources/1/10/10text0.moz-backup +FINISH REMOVEFILE Contents/Resources/0/00/00text1 +backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text1.moz-backup +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/9/97/970/97xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/9/97/970/97xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/9/97/971/97xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/9/97/971/97xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/9/96/96text0 +backup_restore: backup file doesn't exist: Contents/Resources/9/96/96text0.moz-backup +FINISH REMOVEFILE Contents/Resources/9/96/96text1 +backup_restore: backup file doesn't exist: Contents/Resources/9/96/96text1.moz-backup +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/8/87/870/87xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/8/87/870/87xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/8/87/871/87xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/8/87/871/87xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/8/86/86text0 +backup_restore: backup file doesn't exist: Contents/Resources/8/86/86text0.moz-backup +FINISH REMOVEFILE Contents/Resources/8/86/86text1 +backup_restore: backup file doesn't exist: Contents/Resources/8/86/86text1.moz-backup +FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe +backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtest.exe.moz-backup +FINISH REMOVEFILE Contents/Resources/7/70/7xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/7/70/7xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe +backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtest.exe.moz-backup +FINISH REMOVEFILE Contents/Resources/7/71/7xtext0 +backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtext0.moz-backup +FINISH REMOVEFILE Contents/Resources/7/71/7xtext1 +backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtext1.moz-backup +FINISH REMOVEFILE Contents/Resources/7/7text0 +backup_restore: backup file doesn't exist: Contents/Resources/7/7text0.moz-backup +FINISH REMOVEFILE Contents/Resources/7/7text1 +backup_restore: backup file doesn't exist: Contents/Resources/7/7text1.moz-backup +FINISH REMOVEFILE Contents/Resources/5/5text1 +backup_restore: backup file doesn't exist: Contents/Resources/5/5text1.moz-backup +FINISH REMOVEFILE Contents/Resources/5/5text0 +backup_restore: backup file doesn't exist: Contents/Resources/5/5text0.moz-backup +FINISH REMOVEFILE Contents/Resources/5/5test.exe +backup_restore: backup file doesn't exist: Contents/Resources/5/5test.exe.moz-backup +FINISH REMOVEFILE Contents/Resources/5/5text0 +backup_restore: backup file doesn't exist: Contents/Resources/5/5text0.moz-backup +FINISH REMOVEFILE Contents/Resources/5/5text1 +backup_restore: backup file doesn't exist: Contents/Resources/5/5text1.moz-backup +FINISH REMOVEFILE Contents/Resources/4/4text1 +backup_restore: backup file doesn't exist: Contents/Resources/4/4text1.moz-backup +FINISH REMOVEFILE Contents/Resources/4/4text0 +backup_restore: backup file doesn't exist: Contents/Resources/4/4text0.moz-backup +FINISH REMOVEFILE Contents/Resources/3/3text1 +backup_restore: backup file doesn't exist: Contents/Resources/3/3text1.moz-backup +FINISH REMOVEFILE Contents/Resources/3/3text0 +backup_restore: backup file doesn't exist: Contents/Resources/3/3text0.moz-backup +failed: 2 +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/partial_log_failure_win b/toolkit/mozapps/update/tests/data/partial_log_failure_win new file mode 100644 index 0000000000..e3d683dc19 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_log_failure_win @@ -0,0 +1,192 @@ +UPDATE TYPE partial +PREPARE ADD searchplugins/searchpluginstext0 +PREPARE PATCH searchplugins/searchpluginspng1.png +PREPARE PATCH searchplugins/searchpluginspng0.png +PREPARE ADD precomplete +PREPARE PATCH exe0.exe +PREPARE ADD distribution/extensions/extensions1/extensions1text0 +PREPARE PATCH distribution/extensions/extensions1/extensions1png1.png +PREPARE PATCH distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD distribution/extensions/extensions0/extensions0text0 +PREPARE PATCH distribution/extensions/extensions0/extensions0png1.png +PREPARE PATCH distribution/extensions/extensions0/extensions0png0.png +PREPARE PATCH 0/0exe0.exe +PREPARE ADD 0/00/00text0 +PREPARE PATCH 0/00/00png0.png +PREPARE ADD 2/20/20text0 +PREPARE ADD 2/20/20png0.png +PREPARE ADD 0/00/00text2 +PREPARE REMOVEFILE 1/10/10text0 +PREPARE REMOVEFILE 0/00/00text1 +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/98/ +PREPARE REMOVEFILE 9/97/970/97xtext0 +PREPARE REMOVEFILE 9/97/970/97xtext1 +PREPARE REMOVEDIR 9/97/970/ +PREPARE REMOVEFILE 9/97/971/97xtext0 +PREPARE REMOVEFILE 9/97/971/97xtext1 +PREPARE REMOVEDIR 9/97/971/ +PREPARE REMOVEDIR 9/97/ +PREPARE REMOVEFILE 9/96/96text0 +PREPARE REMOVEFILE 9/96/96text1 +PREPARE REMOVEDIR 9/96/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/93/ +PREPARE REMOVEDIR 9/92/ +PREPARE REMOVEDIR 9/91/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/88/ +PREPARE REMOVEFILE 8/87/870/87xtext0 +PREPARE REMOVEFILE 8/87/870/87xtext1 +PREPARE REMOVEDIR 8/87/870/ +PREPARE REMOVEFILE 8/87/871/87xtext0 +PREPARE REMOVEFILE 8/87/871/87xtext1 +PREPARE REMOVEDIR 8/87/871/ +PREPARE REMOVEDIR 8/87/ +PREPARE REMOVEFILE 8/86/86text0 +PREPARE REMOVEFILE 8/86/86text1 +PREPARE REMOVEDIR 8/86/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/83/ +PREPARE REMOVEDIR 8/82/ +PREPARE REMOVEDIR 8/81/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEFILE 7/70/7xtest.exe +PREPARE REMOVEFILE 7/70/7xtext0 +PREPARE REMOVEFILE 7/70/7xtext1 +PREPARE REMOVEDIR 7/70/ +PREPARE REMOVEFILE 7/71/7xtest.exe +PREPARE REMOVEFILE 7/71/7xtext0 +PREPARE REMOVEFILE 7/71/7xtext1 +PREPARE REMOVEDIR 7/71/ +PREPARE REMOVEFILE 7/7text0 +PREPARE REMOVEFILE 7/7text1 +PREPARE REMOVEDIR 7/ +PREPARE REMOVEDIR 6/ +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5test.exe +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEDIR 5/ +PREPARE REMOVEFILE 4/4text1 +PREPARE REMOVEFILE 4/4text0 +PREPARE REMOVEDIR 4/ +PREPARE REMOVEFILE 3/3text1 +PREPARE REMOVEFILE 3/3text0 +PREPARE REMOVEDIR 1/10/ +PREPARE REMOVEDIR 1/ +EXECUTE ADD searchplugins/searchpluginstext0 +EXECUTE PATCH searchplugins/searchpluginspng1.png +EXECUTE PATCH searchplugins/searchpluginspng0.png +EXECUTE ADD precomplete +EXECUTE PATCH exe0.exe +EXECUTE ADD distribution/extensions/extensions1/extensions1text0 +EXECUTE PATCH distribution/extensions/extensions1/extensions1png1.png +EXECUTE PATCH distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD distribution/extensions/extensions0/extensions0text0 +EXECUTE PATCH distribution/extensions/extensions0/extensions0png1.png +EXECUTE PATCH distribution/extensions/extensions0/extensions0png0.png +EXECUTE PATCH 0/0exe0.exe +LoadSourceFile: destination file size 776 does not match expected size 79872 +LoadSourceFile failed +### execution failed +FINISH ADD searchplugins/searchpluginstext0 +FINISH PATCH searchplugins/searchpluginspng1.png +FINISH PATCH searchplugins/searchpluginspng0.png +FINISH ADD precomplete +FINISH PATCH exe0.exe +FINISH ADD distribution/extensions/extensions1/extensions1text0 +backup_restore: backup file doesn't exist: distribution/extensions/extensions1/extensions1text0.moz-backup +FINISH PATCH distribution/extensions/extensions1/extensions1png1.png +FINISH PATCH distribution/extensions/extensions1/extensions1png0.png +FINISH ADD distribution/extensions/extensions0/extensions0text0 +FINISH PATCH distribution/extensions/extensions0/extensions0png1.png +FINISH PATCH distribution/extensions/extensions0/extensions0png0.png +FINISH PATCH 0/0exe0.exe +backup_restore: backup file doesn't exist: 0/0exe0.exe.moz-backup +FINISH ADD 0/00/00text0 +backup_restore: backup file doesn't exist: 0/00/00text0.moz-backup +FINISH PATCH 0/00/00png0.png +backup_restore: backup file doesn't exist: 0/00/00png0.png.moz-backup +FINISH ADD 2/20/20text0 +backup_restore: backup file doesn't exist: 2/20/20text0.moz-backup +FINISH ADD 2/20/20png0.png +backup_restore: backup file doesn't exist: 2/20/20png0.png.moz-backup +FINISH ADD 0/00/00text2 +backup_restore: backup file doesn't exist: 0/00/00text2.moz-backup +FINISH REMOVEFILE 1/10/10text0 +backup_restore: backup file doesn't exist: 1/10/10text0.moz-backup +FINISH REMOVEFILE 0/00/00text1 +backup_restore: backup file doesn't exist: 0/00/00text1.moz-backup +FINISH REMOVEFILE 9/97/970/97xtext0 +backup_restore: backup file doesn't exist: 9/97/970/97xtext0.moz-backup +FINISH REMOVEFILE 9/97/970/97xtext1 +backup_restore: backup file doesn't exist: 9/97/970/97xtext1.moz-backup +FINISH REMOVEFILE 9/97/971/97xtext0 +backup_restore: backup file doesn't exist: 9/97/971/97xtext0.moz-backup +FINISH REMOVEFILE 9/97/971/97xtext1 +backup_restore: backup file doesn't exist: 9/97/971/97xtext1.moz-backup +FINISH REMOVEFILE 9/96/96text0 +backup_restore: backup file doesn't exist: 9/96/96text0.moz-backup +FINISH REMOVEFILE 9/96/96text1 +backup_restore: backup file doesn't exist: 9/96/96text1.moz-backup +FINISH REMOVEFILE 8/87/870/87xtext0 +backup_restore: backup file doesn't exist: 8/87/870/87xtext0.moz-backup +FINISH REMOVEFILE 8/87/870/87xtext1 +backup_restore: backup file doesn't exist: 8/87/870/87xtext1.moz-backup +FINISH REMOVEFILE 8/87/871/87xtext0 +backup_restore: backup file doesn't exist: 8/87/871/87xtext0.moz-backup +FINISH REMOVEFILE 8/87/871/87xtext1 +backup_restore: backup file doesn't exist: 8/87/871/87xtext1.moz-backup +FINISH REMOVEFILE 8/86/86text0 +backup_restore: backup file doesn't exist: 8/86/86text0.moz-backup +FINISH REMOVEFILE 8/86/86text1 +backup_restore: backup file doesn't exist: 8/86/86text1.moz-backup +FINISH REMOVEFILE 7/70/7xtest.exe +backup_restore: backup file doesn't exist: 7/70/7xtest.exe.moz-backup +FINISH REMOVEFILE 7/70/7xtext0 +backup_restore: backup file doesn't exist: 7/70/7xtext0.moz-backup +FINISH REMOVEFILE 7/70/7xtext1 +backup_restore: backup file doesn't exist: 7/70/7xtext1.moz-backup +FINISH REMOVEFILE 7/71/7xtest.exe +backup_restore: backup file doesn't exist: 7/71/7xtest.exe.moz-backup +FINISH REMOVEFILE 7/71/7xtext0 +backup_restore: backup file doesn't exist: 7/71/7xtext0.moz-backup +FINISH REMOVEFILE 7/71/7xtext1 +backup_restore: backup file doesn't exist: 7/71/7xtext1.moz-backup +FINISH REMOVEFILE 7/7text0 +backup_restore: backup file doesn't exist: 7/7text0.moz-backup +FINISH REMOVEFILE 7/7text1 +backup_restore: backup file doesn't exist: 7/7text1.moz-backup +FINISH REMOVEFILE 5/5text1 +backup_restore: backup file doesn't exist: 5/5text1.moz-backup +FINISH REMOVEFILE 5/5text0 +backup_restore: backup file doesn't exist: 5/5text0.moz-backup +FINISH REMOVEFILE 5/5test.exe +backup_restore: backup file doesn't exist: 5/5test.exe.moz-backup +FINISH REMOVEFILE 5/5text0 +backup_restore: backup file doesn't exist: 5/5text0.moz-backup +FINISH REMOVEFILE 5/5text1 +backup_restore: backup file doesn't exist: 5/5text1.moz-backup +FINISH REMOVEFILE 4/4text1 +backup_restore: backup file doesn't exist: 4/4text1.moz-backup +FINISH REMOVEFILE 4/4text0 +backup_restore: backup file doesn't exist: 4/4text0.moz-backup +FINISH REMOVEFILE 3/3text1 +backup_restore: backup file doesn't exist: 3/3text1.moz-backup +FINISH REMOVEFILE 3/3text0 +backup_restore: backup file doesn't exist: 3/3text0.moz-backup +failed: 2 +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/partial_log_success_mac b/toolkit/mozapps/update/tests/data/partial_log_success_mac new file mode 100644 index 0000000000..fb5272ad2c --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_log_success_mac @@ -0,0 +1,279 @@ +UPDATE TYPE partial +PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0 +PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng1.png +PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng0.png +PREPARE ADD Contents/Resources/precomplete +PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +PREPARE PATCH Contents/Resources/0/0exe0.exe +PREPARE ADD Contents/Resources/0/00/00text0 +PREPARE PATCH Contents/Resources/0/00/00png0.png +PREPARE PATCH Contents/MacOS/exe0.exe +PREPARE ADD Contents/Resources/2/20/20text0 +PREPARE ADD Contents/Resources/2/20/20png0.png +PREPARE ADD Contents/Resources/0/00/00text2 +PREPARE REMOVEFILE Contents/Resources/1/10/10text0 +PREPARE REMOVEFILE Contents/Resources/0/00/00text1 +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/99/ +PREPARE REMOVEDIR Contents/Resources/9/98/ +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/970/ +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0 +PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1 +PREPARE REMOVEDIR Contents/Resources/9/97/971/ +PREPARE REMOVEDIR Contents/Resources/9/97/ +PREPARE REMOVEFILE Contents/Resources/9/96/96text0 +PREPARE REMOVEFILE Contents/Resources/9/96/96text1 +PREPARE REMOVEDIR Contents/Resources/9/96/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/95/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/94/ +PREPARE REMOVEDIR Contents/Resources/9/93/ +PREPARE REMOVEDIR Contents/Resources/9/92/ +PREPARE REMOVEDIR Contents/Resources/9/91/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/9/90/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/89/ +PREPARE REMOVEDIR Contents/Resources/8/88/ +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/870/ +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0 +PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1 +PREPARE REMOVEDIR Contents/Resources/8/87/871/ +PREPARE REMOVEDIR Contents/Resources/8/87/ +PREPARE REMOVEFILE Contents/Resources/8/86/86text0 +PREPARE REMOVEFILE Contents/Resources/8/86/86text1 +PREPARE REMOVEDIR Contents/Resources/8/86/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/85/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/84/ +PREPARE REMOVEDIR Contents/Resources/8/83/ +PREPARE REMOVEDIR Contents/Resources/8/82/ +PREPARE REMOVEDIR Contents/Resources/8/81/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEDIR Contents/Resources/8/80/ +PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/70/ +PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0 +PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1 +PREPARE REMOVEDIR Contents/Resources/7/71/ +PREPARE REMOVEFILE Contents/Resources/7/7text0 +PREPARE REMOVEFILE Contents/Resources/7/7text1 +PREPARE REMOVEDIR Contents/Resources/7/ +PREPARE REMOVEDIR Contents/Resources/6/ +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5test.exe +PREPARE REMOVEFILE Contents/Resources/5/5text0 +PREPARE REMOVEFILE Contents/Resources/5/5text1 +PREPARE REMOVEDIR Contents/Resources/5/ +PREPARE REMOVEFILE Contents/Resources/4/4text1 +PREPARE REMOVEFILE Contents/Resources/4/4text0 +PREPARE REMOVEDIR Contents/Resources/4/ +PREPARE REMOVEFILE Contents/Resources/3/3text1 +PREPARE REMOVEFILE Contents/Resources/3/3text0 +PREPARE REMOVEDIR Contents/Resources/1/10/ +PREPARE REMOVEDIR Contents/Resources/1/ +EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0 +EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng1.png +EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng0.png +EXECUTE ADD Contents/Resources/precomplete +EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +EXECUTE PATCH Contents/Resources/0/0exe0.exe +EXECUTE ADD Contents/Resources/0/00/00text0 +EXECUTE PATCH Contents/Resources/0/00/00png0.png +EXECUTE PATCH Contents/MacOS/exe0.exe +EXECUTE ADD Contents/Resources/2/20/20text0 +EXECUTE ADD Contents/Resources/2/20/20png0.png +EXECUTE ADD Contents/Resources/0/00/00text2 +EXECUTE REMOVEFILE Contents/Resources/1/10/10text0 +EXECUTE REMOVEFILE Contents/Resources/0/00/00text1 +EXECUTE REMOVEDIR Contents/Resources/9/99/ +EXECUTE REMOVEDIR Contents/Resources/9/99/ +EXECUTE REMOVEDIR Contents/Resources/9/98/ +EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext0 +EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext1 +EXECUTE REMOVEDIR Contents/Resources/9/97/970/ +EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext0 +EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext1 +EXECUTE REMOVEDIR Contents/Resources/9/97/971/ +EXECUTE REMOVEDIR Contents/Resources/9/97/ +EXECUTE REMOVEFILE Contents/Resources/9/96/96text0 +EXECUTE REMOVEFILE Contents/Resources/9/96/96text1 +EXECUTE REMOVEDIR Contents/Resources/9/96/ +EXECUTE REMOVEDIR Contents/Resources/9/95/ +EXECUTE REMOVEDIR Contents/Resources/9/95/ +EXECUTE REMOVEDIR Contents/Resources/9/94/ +EXECUTE REMOVEDIR Contents/Resources/9/94/ +EXECUTE REMOVEDIR Contents/Resources/9/93/ +EXECUTE REMOVEDIR Contents/Resources/9/92/ +EXECUTE REMOVEDIR Contents/Resources/9/91/ +EXECUTE REMOVEDIR Contents/Resources/9/90/ +EXECUTE REMOVEDIR Contents/Resources/9/90/ +EXECUTE REMOVEDIR Contents/Resources/8/89/ +EXECUTE REMOVEDIR Contents/Resources/8/89/ +EXECUTE REMOVEDIR Contents/Resources/8/88/ +EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext0 +EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext1 +EXECUTE REMOVEDIR Contents/Resources/8/87/870/ +EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext0 +EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext1 +EXECUTE REMOVEDIR Contents/Resources/8/87/871/ +EXECUTE REMOVEDIR Contents/Resources/8/87/ +EXECUTE REMOVEFILE Contents/Resources/8/86/86text0 +EXECUTE REMOVEFILE Contents/Resources/8/86/86text1 +EXECUTE REMOVEDIR Contents/Resources/8/86/ +EXECUTE REMOVEDIR Contents/Resources/8/85/ +EXECUTE REMOVEDIR Contents/Resources/8/85/ +EXECUTE REMOVEDIR Contents/Resources/8/84/ +EXECUTE REMOVEDIR Contents/Resources/8/84/ +EXECUTE REMOVEDIR Contents/Resources/8/83/ +EXECUTE REMOVEDIR Contents/Resources/8/82/ +EXECUTE REMOVEDIR Contents/Resources/8/81/ +EXECUTE REMOVEDIR Contents/Resources/8/80/ +EXECUTE REMOVEDIR Contents/Resources/8/80/ +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtest.exe +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext0 +EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext1 +EXECUTE REMOVEDIR Contents/Resources/7/70/ +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtest.exe +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext0 +EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext1 +EXECUTE REMOVEDIR Contents/Resources/7/71/ +EXECUTE REMOVEFILE Contents/Resources/7/7text0 +EXECUTE REMOVEFILE Contents/Resources/7/7text1 +EXECUTE REMOVEDIR Contents/Resources/7/ +EXECUTE REMOVEDIR Contents/Resources/6/ +EXECUTE REMOVEFILE Contents/Resources/5/5text1 +EXECUTE REMOVEFILE Contents/Resources/5/5text0 +EXECUTE REMOVEFILE Contents/Resources/5/5test.exe +EXECUTE REMOVEFILE Contents/Resources/5/5text0 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEFILE Contents/Resources/5/5text1 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEDIR Contents/Resources/5/ +EXECUTE REMOVEFILE Contents/Resources/4/4text1 +EXECUTE REMOVEFILE Contents/Resources/4/4text0 +EXECUTE REMOVEDIR Contents/Resources/4/ +EXECUTE REMOVEFILE Contents/Resources/3/3text1 +EXECUTE REMOVEFILE Contents/Resources/3/3text0 +EXECUTE REMOVEDIR Contents/Resources/1/10/ +EXECUTE REMOVEDIR Contents/Resources/1/ +FINISH ADD Contents/Resources/searchplugins/searchpluginstext0 +FINISH PATCH Contents/Resources/searchplugins/searchpluginspng1.png +FINISH PATCH Contents/Resources/searchplugins/searchpluginspng0.png +FINISH ADD Contents/Resources/precomplete +FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0 +FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png +FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png +FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0 +FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png +FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png +FINISH PATCH Contents/Resources/0/0exe0.exe +FINISH ADD Contents/Resources/0/00/00text0 +FINISH PATCH Contents/Resources/0/00/00png0.png +FINISH PATCH Contents/MacOS/exe0.exe +FINISH ADD Contents/Resources/2/20/20text0 +FINISH ADD Contents/Resources/2/20/20png0.png +FINISH ADD Contents/Resources/0/00/00text2 +FINISH REMOVEFILE Contents/Resources/1/10/10text0 +FINISH REMOVEFILE Contents/Resources/0/00/00text1 +FINISH REMOVEDIR Contents/Resources/9/99/ +FINISH REMOVEDIR Contents/Resources/9/99/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/98/ +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0 +FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1 +FINISH REMOVEDIR Contents/Resources/9/97/970/ +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0 +FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1 +FINISH REMOVEDIR Contents/Resources/9/97/971/ +FINISH REMOVEDIR Contents/Resources/9/97/ +FINISH REMOVEFILE Contents/Resources/9/96/96text0 +FINISH REMOVEFILE Contents/Resources/9/96/96text1 +FINISH REMOVEDIR Contents/Resources/9/96/ +FINISH REMOVEDIR Contents/Resources/9/95/ +FINISH REMOVEDIR Contents/Resources/9/95/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/94/ +FINISH REMOVEDIR Contents/Resources/9/94/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/9/93/ +FINISH REMOVEDIR Contents/Resources/9/92/ +removing directory: Contents/Resources/9/92/, rv: 0 +FINISH REMOVEDIR Contents/Resources/9/91/ +removing directory: Contents/Resources/9/91/, rv: 0 +FINISH REMOVEDIR Contents/Resources/9/90/ +FINISH REMOVEDIR Contents/Resources/9/90/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/89/ +FINISH REMOVEDIR Contents/Resources/8/89/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/88/ +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0 +FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1 +FINISH REMOVEDIR Contents/Resources/8/87/870/ +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0 +FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1 +FINISH REMOVEDIR Contents/Resources/8/87/871/ +FINISH REMOVEDIR Contents/Resources/8/87/ +FINISH REMOVEFILE Contents/Resources/8/86/86text0 +FINISH REMOVEFILE Contents/Resources/8/86/86text1 +FINISH REMOVEDIR Contents/Resources/8/86/ +FINISH REMOVEDIR Contents/Resources/8/85/ +FINISH REMOVEDIR Contents/Resources/8/85/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/84/ +FINISH REMOVEDIR Contents/Resources/8/84/ +directory no longer exists; skipping +FINISH REMOVEDIR Contents/Resources/8/83/ +FINISH REMOVEDIR Contents/Resources/8/82/ +removing directory: Contents/Resources/8/82/, rv: 0 +FINISH REMOVEDIR Contents/Resources/8/81/ +removing directory: Contents/Resources/8/81/, rv: 0 +FINISH REMOVEDIR Contents/Resources/8/80/ +FINISH REMOVEDIR Contents/Resources/8/80/ +directory no longer exists; skipping +FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe +FINISH REMOVEFILE Contents/Resources/7/70/7xtext0 +FINISH REMOVEFILE Contents/Resources/7/70/7xtext1 +FINISH REMOVEDIR Contents/Resources/7/70/ +FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe +FINISH REMOVEFILE Contents/Resources/7/71/7xtext0 +FINISH REMOVEFILE Contents/Resources/7/71/7xtext1 +FINISH REMOVEDIR Contents/Resources/7/71/ +FINISH REMOVEFILE Contents/Resources/7/7text0 +FINISH REMOVEFILE Contents/Resources/7/7text1 +FINISH REMOVEDIR Contents/Resources/7/ +FINISH REMOVEDIR Contents/Resources/6/ +FINISH REMOVEFILE Contents/Resources/5/5text1 +FINISH REMOVEFILE Contents/Resources/5/5text0 +FINISH REMOVEFILE Contents/Resources/5/5test.exe +FINISH REMOVEDIR Contents/Resources/5/ +FINISH REMOVEFILE Contents/Resources/4/4text1 +FINISH REMOVEFILE Contents/Resources/4/4text0 +FINISH REMOVEDIR Contents/Resources/4/ +FINISH REMOVEFILE Contents/Resources/3/3text1 +FINISH REMOVEFILE Contents/Resources/3/3text0 +FINISH REMOVEDIR Contents/Resources/1/10/ +FINISH REMOVEDIR Contents/Resources/1/ +succeeded +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/partial_log_success_win b/toolkit/mozapps/update/tests/data/partial_log_success_win new file mode 100644 index 0000000000..1f5c4b3b49 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_log_success_win @@ -0,0 +1,279 @@ +UPDATE TYPE partial +PREPARE ADD searchplugins/searchpluginstext0 +PREPARE PATCH searchplugins/searchpluginspng1.png +PREPARE PATCH searchplugins/searchpluginspng0.png +PREPARE ADD precomplete +PREPARE PATCH exe0.exe +PREPARE ADD distribution/extensions/extensions1/extensions1text0 +PREPARE PATCH distribution/extensions/extensions1/extensions1png1.png +PREPARE PATCH distribution/extensions/extensions1/extensions1png0.png +PREPARE ADD distribution/extensions/extensions0/extensions0text0 +PREPARE PATCH distribution/extensions/extensions0/extensions0png1.png +PREPARE PATCH distribution/extensions/extensions0/extensions0png0.png +PREPARE PATCH 0/0exe0.exe +PREPARE ADD 0/00/00text0 +PREPARE PATCH 0/00/00png0.png +PREPARE ADD 2/20/20text0 +PREPARE ADD 2/20/20png0.png +PREPARE ADD 0/00/00text2 +PREPARE REMOVEFILE 1/10/10text0 +PREPARE REMOVEFILE 0/00/00text1 +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/99/ +PREPARE REMOVEDIR 9/98/ +PREPARE REMOVEFILE 9/97/970/97xtext0 +PREPARE REMOVEFILE 9/97/970/97xtext1 +PREPARE REMOVEDIR 9/97/970/ +PREPARE REMOVEFILE 9/97/971/97xtext0 +PREPARE REMOVEFILE 9/97/971/97xtext1 +PREPARE REMOVEDIR 9/97/971/ +PREPARE REMOVEDIR 9/97/ +PREPARE REMOVEFILE 9/96/96text0 +PREPARE REMOVEFILE 9/96/96text1 +PREPARE REMOVEDIR 9/96/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/95/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/94/ +PREPARE REMOVEDIR 9/93/ +PREPARE REMOVEDIR 9/92/ +PREPARE REMOVEDIR 9/91/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 9/90/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/89/ +PREPARE REMOVEDIR 8/88/ +PREPARE REMOVEFILE 8/87/870/87xtext0 +PREPARE REMOVEFILE 8/87/870/87xtext1 +PREPARE REMOVEDIR 8/87/870/ +PREPARE REMOVEFILE 8/87/871/87xtext0 +PREPARE REMOVEFILE 8/87/871/87xtext1 +PREPARE REMOVEDIR 8/87/871/ +PREPARE REMOVEDIR 8/87/ +PREPARE REMOVEFILE 8/86/86text0 +PREPARE REMOVEFILE 8/86/86text1 +PREPARE REMOVEDIR 8/86/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/85/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/84/ +PREPARE REMOVEDIR 8/83/ +PREPARE REMOVEDIR 8/82/ +PREPARE REMOVEDIR 8/81/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEDIR 8/80/ +PREPARE REMOVEFILE 7/70/7xtest.exe +PREPARE REMOVEFILE 7/70/7xtext0 +PREPARE REMOVEFILE 7/70/7xtext1 +PREPARE REMOVEDIR 7/70/ +PREPARE REMOVEFILE 7/71/7xtest.exe +PREPARE REMOVEFILE 7/71/7xtext0 +PREPARE REMOVEFILE 7/71/7xtext1 +PREPARE REMOVEDIR 7/71/ +PREPARE REMOVEFILE 7/7text0 +PREPARE REMOVEFILE 7/7text1 +PREPARE REMOVEDIR 7/ +PREPARE REMOVEDIR 6/ +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5test.exe +PREPARE REMOVEFILE 5/5text0 +PREPARE REMOVEFILE 5/5text1 +PREPARE REMOVEDIR 5/ +PREPARE REMOVEFILE 4/4text1 +PREPARE REMOVEFILE 4/4text0 +PREPARE REMOVEDIR 4/ +PREPARE REMOVEFILE 3/3text1 +PREPARE REMOVEFILE 3/3text0 +PREPARE REMOVEDIR 1/10/ +PREPARE REMOVEDIR 1/ +EXECUTE ADD searchplugins/searchpluginstext0 +EXECUTE PATCH searchplugins/searchpluginspng1.png +EXECUTE PATCH searchplugins/searchpluginspng0.png +EXECUTE ADD precomplete +EXECUTE PATCH exe0.exe +EXECUTE ADD distribution/extensions/extensions1/extensions1text0 +EXECUTE PATCH distribution/extensions/extensions1/extensions1png1.png +EXECUTE PATCH distribution/extensions/extensions1/extensions1png0.png +EXECUTE ADD distribution/extensions/extensions0/extensions0text0 +EXECUTE PATCH distribution/extensions/extensions0/extensions0png1.png +EXECUTE PATCH distribution/extensions/extensions0/extensions0png0.png +EXECUTE PATCH 0/0exe0.exe +EXECUTE ADD 0/00/00text0 +EXECUTE PATCH 0/00/00png0.png +EXECUTE ADD 2/20/20text0 +EXECUTE ADD 2/20/20png0.png +EXECUTE ADD 0/00/00text2 +EXECUTE REMOVEFILE 1/10/10text0 +EXECUTE REMOVEFILE 0/00/00text1 +EXECUTE REMOVEDIR 9/99/ +EXECUTE REMOVEDIR 9/99/ +EXECUTE REMOVEDIR 9/98/ +EXECUTE REMOVEFILE 9/97/970/97xtext0 +EXECUTE REMOVEFILE 9/97/970/97xtext1 +EXECUTE REMOVEDIR 9/97/970/ +EXECUTE REMOVEFILE 9/97/971/97xtext0 +EXECUTE REMOVEFILE 9/97/971/97xtext1 +EXECUTE REMOVEDIR 9/97/971/ +EXECUTE REMOVEDIR 9/97/ +EXECUTE REMOVEFILE 9/96/96text0 +EXECUTE REMOVEFILE 9/96/96text1 +EXECUTE REMOVEDIR 9/96/ +EXECUTE REMOVEDIR 9/95/ +EXECUTE REMOVEDIR 9/95/ +EXECUTE REMOVEDIR 9/94/ +EXECUTE REMOVEDIR 9/94/ +EXECUTE REMOVEDIR 9/93/ +EXECUTE REMOVEDIR 9/92/ +EXECUTE REMOVEDIR 9/91/ +EXECUTE REMOVEDIR 9/90/ +EXECUTE REMOVEDIR 9/90/ +EXECUTE REMOVEDIR 8/89/ +EXECUTE REMOVEDIR 8/89/ +EXECUTE REMOVEDIR 8/88/ +EXECUTE REMOVEFILE 8/87/870/87xtext0 +EXECUTE REMOVEFILE 8/87/870/87xtext1 +EXECUTE REMOVEDIR 8/87/870/ +EXECUTE REMOVEFILE 8/87/871/87xtext0 +EXECUTE REMOVEFILE 8/87/871/87xtext1 +EXECUTE REMOVEDIR 8/87/871/ +EXECUTE REMOVEDIR 8/87/ +EXECUTE REMOVEFILE 8/86/86text0 +EXECUTE REMOVEFILE 8/86/86text1 +EXECUTE REMOVEDIR 8/86/ +EXECUTE REMOVEDIR 8/85/ +EXECUTE REMOVEDIR 8/85/ +EXECUTE REMOVEDIR 8/84/ +EXECUTE REMOVEDIR 8/84/ +EXECUTE REMOVEDIR 8/83/ +EXECUTE REMOVEDIR 8/82/ +EXECUTE REMOVEDIR 8/81/ +EXECUTE REMOVEDIR 8/80/ +EXECUTE REMOVEDIR 8/80/ +EXECUTE REMOVEFILE 7/70/7xtest.exe +EXECUTE REMOVEFILE 7/70/7xtext0 +EXECUTE REMOVEFILE 7/70/7xtext1 +EXECUTE REMOVEDIR 7/70/ +EXECUTE REMOVEFILE 7/71/7xtest.exe +EXECUTE REMOVEFILE 7/71/7xtext0 +EXECUTE REMOVEFILE 7/71/7xtext1 +EXECUTE REMOVEDIR 7/71/ +EXECUTE REMOVEFILE 7/7text0 +EXECUTE REMOVEFILE 7/7text1 +EXECUTE REMOVEDIR 7/ +EXECUTE REMOVEDIR 6/ +EXECUTE REMOVEFILE 5/5text1 +EXECUTE REMOVEFILE 5/5text0 +EXECUTE REMOVEFILE 5/5test.exe +EXECUTE REMOVEFILE 5/5text0 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEFILE 5/5text1 +file cannot be removed because it does not exist; skipping +EXECUTE REMOVEDIR 5/ +EXECUTE REMOVEFILE 4/4text1 +EXECUTE REMOVEFILE 4/4text0 +EXECUTE REMOVEDIR 4/ +EXECUTE REMOVEFILE 3/3text1 +EXECUTE REMOVEFILE 3/3text0 +EXECUTE REMOVEDIR 1/10/ +EXECUTE REMOVEDIR 1/ +FINISH ADD searchplugins/searchpluginstext0 +FINISH PATCH searchplugins/searchpluginspng1.png +FINISH PATCH searchplugins/searchpluginspng0.png +FINISH ADD precomplete +FINISH PATCH exe0.exe +FINISH ADD distribution/extensions/extensions1/extensions1text0 +FINISH PATCH distribution/extensions/extensions1/extensions1png1.png +FINISH PATCH distribution/extensions/extensions1/extensions1png0.png +FINISH ADD distribution/extensions/extensions0/extensions0text0 +FINISH PATCH distribution/extensions/extensions0/extensions0png1.png +FINISH PATCH distribution/extensions/extensions0/extensions0png0.png +FINISH PATCH 0/0exe0.exe +FINISH ADD 0/00/00text0 +FINISH PATCH 0/00/00png0.png +FINISH ADD 2/20/20text0 +FINISH ADD 2/20/20png0.png +FINISH ADD 0/00/00text2 +FINISH REMOVEFILE 1/10/10text0 +FINISH REMOVEFILE 0/00/00text1 +FINISH REMOVEDIR 9/99/ +FINISH REMOVEDIR 9/99/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/98/ +FINISH REMOVEFILE 9/97/970/97xtext0 +FINISH REMOVEFILE 9/97/970/97xtext1 +FINISH REMOVEDIR 9/97/970/ +FINISH REMOVEFILE 9/97/971/97xtext0 +FINISH REMOVEFILE 9/97/971/97xtext1 +FINISH REMOVEDIR 9/97/971/ +FINISH REMOVEDIR 9/97/ +FINISH REMOVEFILE 9/96/96text0 +FINISH REMOVEFILE 9/96/96text1 +FINISH REMOVEDIR 9/96/ +FINISH REMOVEDIR 9/95/ +FINISH REMOVEDIR 9/95/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/94/ +FINISH REMOVEDIR 9/94/ +directory no longer exists; skipping +FINISH REMOVEDIR 9/93/ +FINISH REMOVEDIR 9/92/ +removing directory: 9/92/, rv: 0 +FINISH REMOVEDIR 9/91/ +removing directory: 9/91/, rv: 0 +FINISH REMOVEDIR 9/90/ +FINISH REMOVEDIR 9/90/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/89/ +FINISH REMOVEDIR 8/89/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/88/ +FINISH REMOVEFILE 8/87/870/87xtext0 +FINISH REMOVEFILE 8/87/870/87xtext1 +FINISH REMOVEDIR 8/87/870/ +FINISH REMOVEFILE 8/87/871/87xtext0 +FINISH REMOVEFILE 8/87/871/87xtext1 +FINISH REMOVEDIR 8/87/871/ +FINISH REMOVEDIR 8/87/ +FINISH REMOVEFILE 8/86/86text0 +FINISH REMOVEFILE 8/86/86text1 +FINISH REMOVEDIR 8/86/ +FINISH REMOVEDIR 8/85/ +FINISH REMOVEDIR 8/85/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/84/ +FINISH REMOVEDIR 8/84/ +directory no longer exists; skipping +FINISH REMOVEDIR 8/83/ +FINISH REMOVEDIR 8/82/ +removing directory: 8/82/, rv: 0 +FINISH REMOVEDIR 8/81/ +removing directory: 8/81/, rv: 0 +FINISH REMOVEDIR 8/80/ +FINISH REMOVEDIR 8/80/ +directory no longer exists; skipping +FINISH REMOVEFILE 7/70/7xtest.exe +FINISH REMOVEFILE 7/70/7xtext0 +FINISH REMOVEFILE 7/70/7xtext1 +FINISH REMOVEDIR 7/70/ +FINISH REMOVEFILE 7/71/7xtest.exe +FINISH REMOVEFILE 7/71/7xtext0 +FINISH REMOVEFILE 7/71/7xtext1 +FINISH REMOVEDIR 7/71/ +FINISH REMOVEFILE 7/7text0 +FINISH REMOVEFILE 7/7text1 +FINISH REMOVEDIR 7/ +FINISH REMOVEDIR 6/ +FINISH REMOVEFILE 5/5text1 +FINISH REMOVEFILE 5/5text0 +FINISH REMOVEFILE 5/5test.exe +FINISH REMOVEDIR 5/ +FINISH REMOVEFILE 4/4text1 +FINISH REMOVEFILE 4/4text0 +FINISH REMOVEDIR 4/ +FINISH REMOVEFILE 3/3text1 +FINISH REMOVEFILE 3/3text0 +FINISH REMOVEDIR 1/10/ +FINISH REMOVEDIR 1/ +succeeded +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/partial_mac.mar b/toolkit/mozapps/update/tests/data/partial_mac.mar Binary files differnew file mode 100644 index 0000000000..bcc04b9939 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_mac.mar diff --git a/toolkit/mozapps/update/tests/data/partial_precomplete b/toolkit/mozapps/update/tests/data/partial_precomplete new file mode 100644 index 0000000000..3ec201463a --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_precomplete @@ -0,0 +1,19 @@ +remove "searchplugins/searchpluginstext0" +remove "searchplugins/searchpluginspng1.png" +remove "searchplugins/searchpluginspng0.png" +remove "removed-files" +remove "precomplete" +remove "exe0.exe" +remove "2/20/20text0" +remove "2/20/20png0.png" +remove "0/0exe0.exe" +remove "0/00/00text2" +remove "0/00/00text0" +remove "0/00/00png0.png" +rmdir "searchplugins/" +rmdir "defaults/pref/" +rmdir "defaults/" +rmdir "2/20/" +rmdir "2/" +rmdir "0/00/" +rmdir "0/" diff --git a/toolkit/mozapps/update/tests/data/partial_precomplete_mac b/toolkit/mozapps/update/tests/data/partial_precomplete_mac new file mode 100644 index 0000000000..c65b6e4e38 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_precomplete_mac @@ -0,0 +1,22 @@ +remove "Contents/Resources/searchplugins/searchpluginstext0" +remove "Contents/Resources/searchplugins/searchpluginspng1.png" +remove "Contents/Resources/searchplugins/searchpluginspng0.png" +remove "Contents/Resources/removed-files" +remove "Contents/Resources/precomplete" +remove "Contents/Resources/2/20/20text0" +remove "Contents/Resources/2/20/20png0.png" +remove "Contents/Resources/0/0exe0.exe" +remove "Contents/Resources/0/00/00text2" +remove "Contents/Resources/0/00/00text0" +remove "Contents/Resources/0/00/00png0.png" +remove "Contents/MacOS/exe0.exe" +rmdir "Contents/Resources/searchplugins/" +rmdir "Contents/Resources/defaults/pref/" +rmdir "Contents/Resources/defaults/" +rmdir "Contents/Resources/2/20/" +rmdir "Contents/Resources/2/" +rmdir "Contents/Resources/0/00/" +rmdir "Contents/Resources/0/" +rmdir "Contents/Resources/" +rmdir "Contents/MacOS/" +rmdir "Contents/" diff --git a/toolkit/mozapps/update/tests/data/partial_removed-files b/toolkit/mozapps/update/tests/data/partial_removed-files new file mode 100644 index 0000000000..881311b82c --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_removed-files @@ -0,0 +1,41 @@ +a/b/text0 +a/b/text1 +a/b/3/3text0 +a/b/3/3text1 +a/b/4/4exe0.exe +a/b/4/4text0 +a/b/4/4text1 +a/b/4/ +a/b/5/5text0 +a/b/5/5text1 +a/b/5/* +a/b/6/ +a/b/7/* +a/b/8/80/ +a/b/8/81/ +a/b/8/82/ +a/b/8/83/ +a/b/8/84/ +a/b/8/85/* +a/b/8/86/* +a/b/8/87/* +a/b/8/88/* +a/b/8/89/* +a/b/8/80/ +a/b/8/84/* +a/b/8/85/* +a/b/8/89/ +a/b/9/90/ +a/b/9/91/ +a/b/9/92/ +a/b/9/93/ +a/b/9/94/ +a/b/9/95/* +a/b/9/96/* +a/b/9/97/* +a/b/9/98/* +a/b/9/99/* +a/b/9/90/ +a/b/9/94/* +a/b/9/95/* +a/b/9/99/ diff --git a/toolkit/mozapps/update/tests/data/partial_removed-files_mac b/toolkit/mozapps/update/tests/data/partial_removed-files_mac new file mode 100644 index 0000000000..955dc5b340 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_removed-files_mac @@ -0,0 +1,41 @@ +Contents/Resources/text0 +Contents/Resources/text1 +Contents/Resources/3/3text0 +Contents/Resources/3/3text1 +Contents/Resources/4/exe0.exe +Contents/Resources/4/4text0 +Contents/Resources/4/4text1 +Contents/Resources/4/ +Contents/Resources/5/5text0 +Contents/Resources/5/5text1 +Contents/Resources/5/* +Contents/Resources/6/ +Contents/Resources/7/* +Contents/Resources/8/80/ +Contents/Resources/8/81/ +Contents/Resources/8/82/ +Contents/Resources/8/83/ +Contents/Resources/8/84/ +Contents/Resources/8/85/* +Contents/Resources/8/86/* +Contents/Resources/8/87/* +Contents/Resources/8/88/* +Contents/Resources/8/89/* +Contents/Resources/8/80/ +Contents/Resources/8/84/* +Contents/Resources/8/85/* +Contents/Resources/8/89/ +Contents/Resources/9/90/ +Contents/Resources/9/91/ +Contents/Resources/9/92/ +Contents/Resources/9/93/ +Contents/Resources/9/94/ +Contents/Resources/9/95/* +Contents/Resources/9/96/* +Contents/Resources/9/97/* +Contents/Resources/9/98/* +Contents/Resources/9/99/* +Contents/Resources/9/90/ +Contents/Resources/9/94/* +Contents/Resources/9/95/* +Contents/Resources/9/99/ diff --git a/toolkit/mozapps/update/tests/data/partial_update_manifest b/toolkit/mozapps/update/tests/data/partial_update_manifest new file mode 100644 index 0000000000..8d4e60ed25 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/partial_update_manifest @@ -0,0 +1,63 @@ +type "partial" +add "precomplete" +add "a/b/searchplugins/searchpluginstext0" +patch-if "a/b/searchplugins/searchpluginspng1.png" "a/b/searchplugins/searchpluginspng1.png.patch" "a/b/searchplugins/searchpluginspng1.png" +patch-if "a/b/searchplugins/searchpluginspng0.png" "a/b/searchplugins/searchpluginspng0.png.patch" "a/b/searchplugins/searchpluginspng0.png" +add-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1text0" +patch-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1png1.png.patch" "a/b/extensions/extensions1/extensions1png1.png" +patch-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1png0.png.patch" "a/b/extensions/extensions1/extensions1png0.png" +add-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0text0" +patch-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0png1.png.patch" "a/b/extensions/extensions0/extensions0png1.png" +patch-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0png0.png.patch" "a/b/extensions/extensions0/extensions0png0.png" +patch "a/b/exe0.exe.patch" "a/b/exe0.exe" +patch "a/b/0/0exe0.exe.patch" "a/b/0/0exe0.exe" +add "a/b/0/00/00text0" +patch "a/b/0/00/00png0.png.patch" "a/b/0/00/00png0.png" +add "a/b/2/20/20text0" +add "a/b/2/20/20png0.png" +add "a/b/0/00/00text2" +remove "a/b/1/10/10text0" +remove "a/b/0/00/00text1" +remove "a/b/text1" +remove "a/b/text0" +rmrfdir "a/b/9/99/" +rmdir "a/b/9/99/" +rmrfdir "a/b/9/98/" +rmrfdir "a/b/9/97/" +rmrfdir "a/b/9/96/" +rmrfdir "a/b/9/95/" +rmrfdir "a/b/9/95/" +rmrfdir "a/b/9/94/" +rmdir "a/b/9/94/" +rmdir "a/b/9/93/" +rmdir "a/b/9/92/" +rmdir "a/b/9/91/" +rmdir "a/b/9/90/" +rmdir "a/b/9/90/" +rmrfdir "a/b/8/89/" +rmdir "a/b/8/89/" +rmrfdir "a/b/8/88/" +rmrfdir "a/b/8/87/" +rmrfdir "a/b/8/86/" +rmrfdir "a/b/8/85/" +rmrfdir "a/b/8/85/" +rmrfdir "a/b/8/84/" +rmdir "a/b/8/84/" +rmdir "a/b/8/83/" +rmdir "a/b/8/82/" +rmdir "a/b/8/81/" +rmdir "a/b/8/80/" +rmdir "a/b/8/80/" +rmrfdir "a/b/7/" +rmdir "a/b/6/" +remove "a/b/5/5text1" +remove "a/b/5/5text0" +rmrfdir "a/b/5/" +remove "a/b/4/4text1" +remove "a/b/4/4text0" +remove "a/b/4/4exe0.exe" +rmdir "a/b/4/" +remove "a/b/3/3text1" +remove "a/b/3/3text0" +rmdir "a/b/1/10/" +rmdir "a/b/1/" diff --git a/toolkit/mozapps/update/tests/data/replace_log_success b/toolkit/mozapps/update/tests/data/replace_log_success new file mode 100644 index 0000000000..323f1db41e --- /dev/null +++ b/toolkit/mozapps/update/tests/data/replace_log_success @@ -0,0 +1,6 @@ +Performing a replace request +rename_file: proceeding to rename the directory +rename_file: proceeding to rename the directory +Now, remove the tmpDir +succeeded +calling QuitProgressUI diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js new file mode 100644 index 0000000000..bca14c8b89 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/shared.js @@ -0,0 +1,928 @@ +/* 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/. */ + +/* Shared code for xpcshell, mochitests-chrome, and mochitest-browser-chrome. */ + +// Definitions needed to run eslint on this file. +/* global AppConstants, DATA_URI_SPEC, LOG_FUNCTION */ +/* global Services, URL_HOST, TestUtils */ + +const { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", + ctypes: "resource://gre/modules/ctypes.sys.mjs", +}); + +const PREF_APP_UPDATE_AUTO = "app.update.auto"; +const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors"; +const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors"; +const PREF_APP_UPDATE_BADGEWAITTIME = "app.update.badgeWaitTime"; +const PREF_APP_UPDATE_BITS_ENABLED = "app.update.BITS.enabled"; +const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations"; +const PREF_APP_UPDATE_CHANNEL = "app.update.channel"; +const PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS = "app.update.download.maxAttempts"; +const PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS = "app.update.download.attempts"; +const PREF_APP_UPDATE_DISABLEDFORTESTING = "app.update.disabledForTesting"; +const PREF_APP_UPDATE_INTERVAL = "app.update.interval"; +const PREF_APP_UPDATE_LASTUPDATETIME = + "app.update.lastUpdateTime.background-update-timer"; +const PREF_APP_UPDATE_LOG = "app.update.log"; +const PREF_APP_UPDATE_NOTIFYDURINGDOWNLOAD = "app.update.notifyDuringDownload"; +const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime"; +const PREF_APP_UPDATE_RETRYTIMEOUT = "app.update.socket.retryTimeout"; +const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled"; +const PREF_APP_UPDATE_SOCKET_MAXERRORS = "app.update.socket.maxErrors"; +const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled"; +const PREF_APP_UPDATE_UNSUPPORTED_URL = "app.update.unsupported.url"; +const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details"; +const PREF_APP_UPDATE_URL_MANUAL = "app.update.url.manual"; +const PREF_APP_UPDATE_LANGPACK_ENABLED = "app.update.langpack.enabled"; + +const PREFBRANCH_APP_PARTNER = "app.partner."; +const PREF_DISTRIBUTION_ID = "distribution.id"; +const PREF_DISTRIBUTION_VERSION = "distribution.version"; + +const CONFIG_APP_UPDATE_AUTO = "app.update.auto"; + +const NS_APP_PROFILE_DIR_STARTUP = "ProfDS"; +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; +const NS_GRE_BIN_DIR = "GreBinD"; +const NS_GRE_DIR = "GreD"; +const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD"; +const XRE_EXECUTABLE_FILE = "XREExeF"; +const XRE_OLD_UPDATE_ROOT_DIR = "OldUpdRootD"; +const XRE_UPDATE_ROOT_DIR = "UpdRootD"; + +const DIR_PATCH = "0"; +const DIR_TOBEDELETED = "tobedeleted"; +const DIR_UPDATES = "updates"; +const DIR_UPDATED = + AppConstants.platform == "macosx" ? "Updated.app" : "updated"; +const DIR_DOWNLOADING = "downloading"; + +const FILE_ACTIVE_UPDATE_XML = "active-update.xml"; +const FILE_ACTIVE_UPDATE_XML_TMP = "active-update.xml.tmp"; +const FILE_APPLICATION_INI = "application.ini"; +const FILE_BACKUP_UPDATE_CONFIG_JSON = "backup-update-config.json"; +const FILE_BACKUP_UPDATE_LOG = "backup-update.log"; +const FILE_BT_RESULT = "bt.result"; +const FILE_LAST_UPDATE_LOG = "last-update.log"; +const FILE_PRECOMPLETE = "precomplete"; +const FILE_PRECOMPLETE_BAK = "precomplete.bak"; +const FILE_UPDATE_CONFIG_JSON = "update-config.json"; +const FILE_UPDATE_LOG = "update.log"; +const FILE_UPDATE_MAR = "update.mar"; +const FILE_UPDATE_SETTINGS_INI = "update-settings.ini"; +const FILE_UPDATE_SETTINGS_INI_BAK = "update-settings.ini.bak"; +const FILE_UPDATE_STATUS = "update.status"; +const FILE_UPDATE_TEST = "update.test"; +const FILE_UPDATE_VERSION = "update.version"; +const FILE_UPDATER_INI = "updater.ini"; +const FILE_UPDATES_XML = "updates.xml"; +const FILE_UPDATES_XML_TMP = "updates.xml.tmp"; + +const UPDATE_SETTINGS_CONTENTS = + "[Settings]\nACCEPTED_MAR_CHANNEL_IDS=xpcshell-test\n"; +const PRECOMPLETE_CONTENTS = 'rmdir "nonexistent_dir/"\n'; + +const PR_RDWR = 0x04; +const PR_CREATE_FILE = 0x08; +const PR_TRUNCATE = 0x20; + +var gChannel; +var gDebugTest = false; + +/* import-globals-from sharedUpdateXML.js */ +Services.scriptloader.loadSubScript(DATA_URI_SPEC + "sharedUpdateXML.js", this); + +const PERMS_FILE = FileUtils.PERMS_FILE; +const PERMS_DIRECTORY = FileUtils.PERMS_DIRECTORY; + +const MODE_WRONLY = FileUtils.MODE_WRONLY; +const MODE_CREATE = FileUtils.MODE_CREATE; +const MODE_APPEND = FileUtils.MODE_APPEND; +const MODE_TRUNCATE = FileUtils.MODE_TRUNCATE; + +const URI_UPDATES_PROPERTIES = + "chrome://mozapps/locale/update/updates.properties"; +const gUpdateBundle = Services.strings.createBundle(URI_UPDATES_PROPERTIES); + +XPCOMUtils.defineLazyGetter(this, "gAUS", function test_gAUS() { + return Cc["@mozilla.org/updates/update-service;1"] + .getService(Ci.nsIApplicationUpdateService) + .QueryInterface(Ci.nsITimerCallback) + .QueryInterface(Ci.nsIObserver); +}); + +XPCOMUtils.defineLazyServiceGetter( + this, + "gUpdateManager", + "@mozilla.org/updates/update-manager;1", + "nsIUpdateManager" +); + +XPCOMUtils.defineLazyServiceGetter( + this, + "gUpdateChecker", + "@mozilla.org/updates/update-checker;1", + "nsIUpdateChecker" +); + +XPCOMUtils.defineLazyGetter(this, "gDefaultPrefBranch", function test_gDPB() { + return Services.prefs.getDefaultBranch(null); +}); + +XPCOMUtils.defineLazyGetter(this, "gPrefRoot", function test_gPR() { + return Services.prefs.getBranch(null); +}); + +/** + * Waits for the specified topic and (optionally) status. + * + * @param topic + * String representing the topic to wait for. + * @param status (optional) + * A string representing the status on said topic to wait for. + * @return A promise which will resolve the first time an event occurs on the + * specified topic, and (optionally) with the specified status. + */ +function waitForEvent(topic, status = null) { + return new Promise(resolve => + Services.obs.addObserver( + { + observe(subject, innerTopic, innerStatus) { + if (!status || status == innerStatus) { + Services.obs.removeObserver(this, topic); + resolve(innerStatus); + } + }, + }, + topic + ) + ); +} + +/* Triggers post-update processing */ +function testPostUpdateProcessing() { + gAUS.observe(null, "test-post-update-processing", ""); +} + +/* Initializes the update service stub */ +function initUpdateServiceStub() { + Cc["@mozilla.org/updates/update-service-stub;1"].createInstance( + Ci.nsISupports + ); +} + +/** + * Reloads the update xml files. + * + * @param skipFiles (optional) + * If true, the update xml files will not be read and the metadata will + * be reset. If false (the default), the update xml files will be read + * to populate the update metadata. + */ +function reloadUpdateManagerData(skipFiles = false) { + let observeData = skipFiles ? "skip-files" : ""; + gUpdateManager + .QueryInterface(Ci.nsIObserver) + .observe(null, "um-reload-update-data", observeData); +} + +const observer = { + observe(aSubject, aTopic, aData) { + if (aTopic == "nsPref:changed" && aData == PREF_APP_UPDATE_CHANNEL) { + let channel = gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL); + if (channel != gChannel) { + debugDump("Changing channel from " + channel + " to " + gChannel); + gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel); + } + } + }, + QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), +}; + +/** + * Sets the app.update.channel preference. + * + * @param aChannel + * The update channel. + */ +function setUpdateChannel(aChannel) { + gChannel = aChannel; + debugDump( + "setting default pref " + PREF_APP_UPDATE_CHANNEL + " to " + gChannel + ); + gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel); + gPrefRoot.addObserver(PREF_APP_UPDATE_CHANNEL, observer); +} + +/** + * Sets the effective update url. + * + * @param aURL + * The update url. If not specified 'URL_HOST + "/update.xml"' will be + * used. + */ +function setUpdateURL(aURL) { + let url = aURL ? aURL : URL_HOST + "/update.xml"; + debugDump("setting update URL to " + url); + + // The Update URL is stored in appinfo. We can replace this process's appinfo + // directly, but that will affect only this process. Luckily, the update URL + // is only ever read from the update process. This means that replacing + // Services.appinfo is sufficient and we don't need to worry about registering + // a replacement factory or anything like that. + let origAppInfo = Services.appinfo; + registerCleanupFunction(() => { + Services.appinfo = origAppInfo; + }); + + // Override the appinfo object with an object that exposes all of the same + // properties overriding just the updateURL. + let mockAppInfo = Object.create(origAppInfo, { + updateURL: { + configurable: true, + enumerable: true, + writable: false, + value: url, + }, + }); + + Services.appinfo = mockAppInfo; +} + +/** + * Writes the updates specified to either the active-update.xml or the + * updates.xml. + * + * @param aContent + * The updates represented as a string to write to the XML file. + * @param isActiveUpdate + * If true this will write to the active-update.xml otherwise it will + * write to the updates.xml file. + */ +function writeUpdatesToXMLFile(aContent, aIsActiveUpdate) { + let file = getUpdateDirFile( + aIsActiveUpdate ? FILE_ACTIVE_UPDATE_XML : FILE_UPDATES_XML + ); + writeFile(file, aContent); +} + +/** + * Writes the current update operation/state to a file in the patch + * directory, indicating to the patching system that operations need + * to be performed. + * + * @param aStatus + * The status value to write. + */ +function writeStatusFile(aStatus) { + let file = getUpdateDirFile(FILE_UPDATE_STATUS); + writeFile(file, aStatus + "\n"); +} + +/** + * Writes the current update version to a file in the patch directory, + * indicating to the patching system the version of the update. + * + * @param aVersion + * The version value to write. + */ +function writeVersionFile(aVersion) { + let file = getUpdateDirFile(FILE_UPDATE_VERSION); + writeFile(file, aVersion + "\n"); +} + +/** + * Writes text to a file. This will replace existing text if the file exists + * and create the file if it doesn't exist. + * + * @param aFile + * The file to write to. Will be created if it doesn't exist. + * @param aText + * The text to write to the file. If there is existing text it will be + * replaced. + */ +function writeFile(aFile, aText) { + let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( + Ci.nsIFileOutputStream + ); + if (!aFile.exists()) { + aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); + } + fos.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0); + fos.write(aText, aText.length); + fos.close(); +} + +/** + * Reads the current update operation/state in the status file in the patch + * directory including the error code if it is present. + * + * @return The status value. + */ +function readStatusFile() { + let file = getUpdateDirFile(FILE_UPDATE_STATUS); + if (!file.exists()) { + debugDump("update status file does not exists! Path: " + file.path); + return STATE_NONE; + } + return readFile(file).split("\n")[0]; +} + +/** + * Reads the current update operation/state in the status file in the patch + * directory without the error code if it is present. + * + * @return The state value. + */ +function readStatusState() { + return readStatusFile().split(": ")[0]; +} + +/** + * Reads the current update operation/state in the status file in the patch + * directory with the error code. + * + * @return The state value. + */ +function readStatusFailedCode() { + return readStatusFile().split(": ")[1]; +} + +/** + * Returns whether or not applying the current update resulted in an error + * verifying binary transparency information. + * + * @return true if there was an error result and false otherwise + */ +function updateHasBinaryTransparencyErrorResult() { + let file = getUpdateDirFile(FILE_BT_RESULT); + return file.exists(); +} + +/** + * Reads text from a file and returns the string. + * + * @param aFile + * The file to read from. + * @return The string of text read from the file. + */ +function readFile(aFile) { + let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + if (!aFile.exists()) { + return null; + } + // Specifying -1 for ioFlags will open the file with the default of PR_RDONLY. + // Specifying -1 for perm will open the file with the default of 0. + fis.init(aFile, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF); + let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( + Ci.nsIScriptableInputStream + ); + sis.init(fis); + let text = sis.read(sis.available()); + sis.close(); + return text; +} + +/* Returns human readable status text from the updates.properties bundle */ +function getStatusText(aErrCode) { + return getString("check_error-" + aErrCode); +} + +/* Returns a string from the updates.properties bundle */ +function getString(aName) { + try { + return gUpdateBundle.GetStringFromName(aName); + } catch (e) {} + return null; +} + +/** + * Gets the file extension for an nsIFile. + * + * @param aFile + * The file to get the file extension for. + * @return The file extension. + */ +function getFileExtension(aFile) { + return Services.io.newFileURI(aFile).QueryInterface(Ci.nsIURL).fileExtension; +} + +/** + * Gets the specified update file or directory. + * + * @param aLogLeafName + * The leafName of the file or directory to get. + * @param aWhichDir + * Since we started having a separate patch directory and downloading + * directory, there are now files with the same name that can be in + * either directory. This argument is optional and defaults to the + * patch directory for historical reasons. But if it is specified as + * DIR_DOWNLOADING, this function will provide the version of the file + * in the downloading directory. For files that aren't in the patch + * directory or the downloading directory, this value is ignored. + * @return nsIFile for the file or directory. + */ +function getUpdateDirFile(aLeafName, aWhichDir = null) { + let file = Services.dirsvc.get(XRE_UPDATE_ROOT_DIR, Ci.nsIFile); + switch (aLeafName) { + case undefined: + return file; + case DIR_UPDATES: + case FILE_ACTIVE_UPDATE_XML: + case FILE_ACTIVE_UPDATE_XML_TMP: + case FILE_UPDATE_CONFIG_JSON: + case FILE_BACKUP_UPDATE_CONFIG_JSON: + case FILE_UPDATE_TEST: + case FILE_UPDATES_XML: + case FILE_UPDATES_XML_TMP: + file.append(aLeafName); + return file; + case DIR_PATCH: + case DIR_DOWNLOADING: + case FILE_BACKUP_UPDATE_LOG: + case FILE_LAST_UPDATE_LOG: + file.append(DIR_UPDATES); + file.append(aLeafName); + return file; + case FILE_BT_RESULT: + case FILE_UPDATE_LOG: + case FILE_UPDATE_MAR: + case FILE_UPDATE_STATUS: + case FILE_UPDATE_VERSION: + case FILE_UPDATER_INI: + file.append(DIR_UPDATES); + if (aWhichDir == DIR_DOWNLOADING) { + file.append(DIR_DOWNLOADING); + } else { + file.append(DIR_PATCH); + } + file.append(aLeafName); + return file; + } + + throw new Error( + "The leafName specified is not handled by this function, " + + "leafName: " + + aLeafName + ); +} + +/** + * Helper function for getting the nsIFile for a file in the directory where the + * update will be staged. + * + * The files for the update are located two directories below the stage + * directory since Mac OS X 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 stage directory. If not specified the stage directory will be + * returned. + * @return The nsIFile for the file in the directory where the update will be + * staged. + */ +function getStageDirFile(aRelPath) { + let file; + if (AppConstants.platform == "macosx") { + file = getUpdateDirFile(DIR_PATCH); + } else { + file = getGREBinDir(); + } + file.append(DIR_UPDATED); + if (aRelPath) { + let pathParts = aRelPath.split("/"); + for (let i = 0; i < pathParts.length; i++) { + if (pathParts[i]) { + file.append(pathParts[i]); + } + } + } + return file; +} + +/** + * Removes the update files that typically need to be removed by tests without + * removing the directories since removing the directories has caused issues + * when running tests with --verify and recursively removes the stage directory. + * + * @param aRemoveLogFiles + * When true the update log files will also be removed. This allows + * for the inspection of the log files while troubleshooting tests. + */ +function removeUpdateFiles(aRemoveLogFiles) { + let files = [ + [FILE_ACTIVE_UPDATE_XML], + [FILE_UPDATES_XML], + [FILE_BT_RESULT], + [FILE_UPDATE_STATUS], + [FILE_UPDATE_VERSION], + [FILE_UPDATE_MAR], + [FILE_UPDATE_MAR, DIR_DOWNLOADING], + [FILE_UPDATER_INI], + ]; + + if (aRemoveLogFiles) { + files = files.concat([ + [FILE_BACKUP_UPDATE_LOG], + [FILE_LAST_UPDATE_LOG], + [FILE_UPDATE_LOG], + ]); + } + + for (let i = 0; i < files.length; i++) { + let file = getUpdateDirFile.apply(null, files[i]); + try { + if (file.exists()) { + file.remove(false); + } + } catch (e) { + logTestInfo( + "Unable to remove file. Path: " + file.path + ", Exception: " + e + ); + } + } + + let stageDir = getStageDirFile(); + if (stageDir.exists()) { + try { + removeDirRecursive(stageDir); + } catch (e) { + logTestInfo( + "Unable to remove directory. Path: " + + stageDir.path + + ", Exception: " + + e + ); + } + } +} + +/** + * Deletes a directory and its children. First it tries nsIFile::Remove(true). + * If that fails it will fall back to recursing, setting the appropriate + * permissions, and deleting the current entry. + * + * @param aDir + * nsIFile for the directory to be deleted. + */ +function removeDirRecursive(aDir) { + if (!aDir.exists()) { + return; + } + + if (!aDir.isDirectory()) { + throw new Error("Only a directory can be passed to this funtion!"); + } + + try { + debugDump("attempting to remove directory. Path: " + aDir.path); + aDir.remove(true); + return; + } catch (e) { + logTestInfo("non-fatal error removing directory. Exception: " + e); + } + + let dirEntries = aDir.directoryEntries; + while (dirEntries.hasMoreElements()) { + let entry = dirEntries.nextFile; + + if (entry.isDirectory()) { + removeDirRecursive(entry); + } else { + entry.permissions = PERMS_FILE; + try { + debugDump("attempting to remove file. Path: " + entry.path); + entry.remove(false); + } catch (e) { + logTestInfo("error removing file. Exception: " + e); + throw e; + } + } + } + + aDir.permissions = PERMS_DIRECTORY; + try { + debugDump("attempting to remove directory. Path: " + aDir.path); + aDir.remove(true); + } catch (e) { + logTestInfo("error removing directory. Exception: " + e); + throw e; + } +} + +/** + * Returns the directory for the currently running process. This is used to + * clean up after the tests and to locate the active-update.xml and updates.xml + * files. + * + * @return nsIFile for the current process directory. + */ +function getCurrentProcessDir() { + return Services.dirsvc.get(NS_XPCOM_CURRENT_PROCESS_DIR, Ci.nsIFile); +} + +/** + * Returns the Gecko Runtime Engine directory where files other than executable + * binaries are located. On Mac OS X this will be <bundle>/Contents/Resources/ + * and the installation directory on all other platforms. + * + * @return nsIFile for the Gecko Runtime Engine directory. + */ +function getGREDir() { + return Services.dirsvc.get(NS_GRE_DIR, Ci.nsIFile); +} + +/** + * Returns the Gecko Runtime Engine Binary directory where the executable + * binaries are located such as the updater binary (Windows and Linux) or + * updater package (Mac OS X). On Mac OS X this will be + * <bundle>/Contents/MacOS/ and the installation directory on all other + * platforms. + * + * @return nsIFile for the Gecko Runtime Engine Binary directory. + */ +function getGREBinDir() { + return Services.dirsvc.get(NS_GRE_BIN_DIR, Ci.nsIFile); +} + +/** + * Gets the unique mutex name for the installation. + * + * @return Global mutex path. + * @throws If the function is called on a platform other than Windows. + */ +function getPerInstallationMutexName() { + if (AppConstants.platform != "win") { + throw new Error("Windows only function called by a different platform!"); + } + + let hasher = Cc["@mozilla.org/security/hash;1"].createInstance( + Ci.nsICryptoHash + ); + hasher.init(hasher.SHA1); + + let exeFile = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile); + let converter = Cc[ + "@mozilla.org/intl/scriptableunicodeconverter" + ].createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + let data = converter.convertToByteArray(exeFile.path.toLowerCase()); + + hasher.update(data, data.length); + return "Global\\MozillaUpdateMutex-" + hasher.finish(true); +} + +/** + * Closes a Win32 handle. + * + * @param aHandle + * The handle to close. + * @throws If the function is called on a platform other than Windows. + */ +function closeHandle(aHandle) { + if (AppConstants.platform != "win") { + throw new Error("Windows only function called by a different platform!"); + } + + let lib = ctypes.open("kernel32.dll"); + let CloseHandle = lib.declare( + "CloseHandle", + ctypes.winapi_abi, + ctypes.int32_t /* success */, + ctypes.void_t.ptr + ); /* handle */ + CloseHandle(aHandle); + lib.close(); +} + +/** + * Creates a mutex. + * + * @param aName + * The name for the mutex. + * @return The Win32 handle to the mutex. + * @throws If the function is called on a platform other than Windows. + */ +function createMutex(aName) { + if (AppConstants.platform != "win") { + throw new Error("Windows only function called by a different platform!"); + } + + const INITIAL_OWN = 1; + const ERROR_ALREADY_EXISTS = 0xb7; + let lib = ctypes.open("kernel32.dll"); + let CreateMutexW = lib.declare( + "CreateMutexW", + ctypes.winapi_abi, + ctypes.void_t.ptr /* return handle */, + ctypes.void_t.ptr /* security attributes */, + ctypes.int32_t /* initial owner */, + ctypes.char16_t.ptr + ); /* name */ + + let handle = CreateMutexW(null, INITIAL_OWN, aName); + lib.close(); + let alreadyExists = ctypes.winLastError == ERROR_ALREADY_EXISTS; + if (handle && !handle.isNull() && alreadyExists) { + closeHandle(handle); + handle = null; + } + + if (handle && handle.isNull()) { + handle = null; + } + + return handle; +} + +/** + * Synchronously writes the value of the app.update.auto setting to the update + * configuration file on Windows or to a user preference on other platforms. + * When the value passed to this function is null or undefined it will remove + * the configuration file on Windows or the user preference on other platforms. + * + * @param aEnabled + * Possible values are true, false, null, and undefined. When true or + * false this value will be written for app.update.auto in the update + * configuration file on Windows or to the user preference on other + * platforms. When null or undefined the update configuration file will + * be removed on Windows or the user preference will be removed on other + * platforms. + */ +function setAppUpdateAutoSync(aEnabled) { + if (AppConstants.platform == "win") { + let file = getUpdateDirFile(FILE_UPDATE_CONFIG_JSON); + if (aEnabled === undefined || aEnabled === null) { + if (file.exists()) { + file.remove(false); + } + } else { + writeFile( + file, + '{"' + CONFIG_APP_UPDATE_AUTO + '":' + aEnabled.toString() + "}" + ); + } + } else if (aEnabled === undefined || aEnabled === null) { + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_AUTO)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_AUTO); + } + } else { + Services.prefs.setBoolPref(PREF_APP_UPDATE_AUTO, aEnabled); + } +} + +/** + * Logs TEST-INFO messages. + * + * @param aText + * The text to log. + * @param aCaller (optional) + * An optional Components.stack.caller. If not specified + * Components.stack.caller will be used. + */ +function logTestInfo(aText, aCaller) { + let caller = aCaller ? aCaller : Components.stack.caller; + let now = new Date(); + let hh = now.getHours(); + let mm = now.getMinutes(); + let ss = now.getSeconds(); + let ms = now.getMilliseconds(); + let time = + (hh < 10 ? "0" + hh : hh) + + ":" + + (mm < 10 ? "0" + mm : mm) + + ":" + + (ss < 10 ? "0" + ss : ss) + + ":"; + if (ms < 10) { + time += "00"; + } else if (ms < 100) { + time += "0"; + } + time += ms; + let msg = + time + + " | TEST-INFO | " + + caller.filename + + " | [" + + caller.name + + " : " + + caller.lineNumber + + "] " + + aText; + LOG_FUNCTION(msg); +} + +/** + * Logs TEST-INFO messages when gDebugTest evaluates to true. + * + * @param aText + * The text to log. + * @param aCaller (optional) + * An optional Components.stack.caller. If not specified + * Components.stack.caller will be used. + */ +function debugDump(aText, aCaller) { + if (gDebugTest) { + let caller = aCaller ? aCaller : Components.stack.caller; + logTestInfo(aText, caller); + } +} + +/** + * Creates the continue file used to signal that update staging or the mock http + * server should continue. The delay this creates allows the tests to verify the + * user interfaces before they auto advance to other phases of an update. The + * continue file for staging will be deleted by the test updater and the + * continue file for the update check and update download requests will be + * deleted by the test http server handler implemented in app_update.sjs. The + * test returns a promise so the test can wait on the deletion of the continue + * file when necessary. If the continue file still exists at the end of a test + * it will be removed to prevent it from affecting tests that run after the test + * that created it. + * + * @param leafName + * The leafName of the file to create. This should be one of the + * folowing constants that are defined in testConstants.js: + * CONTINUE_CHECK + * CONTINUE_DOWNLOAD + * CONTINUE_STAGING + * @return Promise + * Resolves when the file is deleted or if the file is not deleted when + * the check for the file's existence times out. If the file isn't + * deleted before the check for the file's existence times out it will + * be deleted when the test ends so it doesn't affect tests that run + * after the test that created the continue file. + * @throws If the file already exists. + */ +async function continueFileHandler(leafName) { + // The total time to wait with 300 retries and the default interval of 100 is + // approximately 30 seconds. + let interval = 100; + let retries = 300; + let continueFile; + if (leafName == CONTINUE_STAGING) { + // The total time to wait with 600 retries and an interval of 200 is + // approximately 120 seconds. + interval = 200; + retries = 600; + continueFile = getGREBinDir(); + if (AppConstants.platform == "macosx") { + continueFile = continueFile.parent.parent; + } + continueFile.append(leafName); + } else { + continueFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + let continuePath = REL_PATH_DATA + leafName; + let continuePathParts = continuePath.split("/"); + for (let i = 0; i < continuePathParts.length; ++i) { + continueFile.append(continuePathParts[i]); + } + } + if (continueFile.exists()) { + logTestInfo( + "The continue file should not exist, path: " + continueFile.path + ); + continueFile.remove(false); + } + debugDump("Creating continue file, path: " + continueFile.path); + continueFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); + // If for whatever reason the continue file hasn't been removed when a test + // has finished remove it during cleanup so it doesn't affect tests that run + // after the test that created it. + registerCleanupFunction(() => { + if (continueFile.exists()) { + logTestInfo( + "Removing continue file during test cleanup, path: " + continueFile.path + ); + continueFile.remove(false); + } + }); + return TestUtils.waitForCondition( + () => !continueFile.exists(), + "Waiting for file to be deleted, path: " + continueFile.path, + interval, + retries + ).catch(e => { + logTestInfo( + "Continue file was not removed after checking " + + retries + + " times, path: " + + continueFile.path + ); + }); +} diff --git a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js new file mode 100644 index 0000000000..acff4aec3f --- /dev/null +++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js @@ -0,0 +1,417 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/** + * Shared code for xpcshell, mochitests-chrome, mochitest-browser-chrome, and + * SJS server-side scripts for the test http server. + */ + +/** + * Helper functions for creating xml strings used by application update tests. + */ + +/* import-globals-from ../browser/testConstants.js */ + +/* global Services, UpdateUtils, gURLData */ + +const FILE_SIMPLE_MAR = "simple.mar"; +const SIZE_SIMPLE_MAR = "1419"; + +const STATE_NONE = "null"; +const STATE_DOWNLOADING = "downloading"; +const STATE_PENDING = "pending"; +const STATE_PENDING_SVC = "pending-service"; +const STATE_PENDING_ELEVATE = "pending-elevate"; +const STATE_APPLYING = "applying"; +const STATE_APPLIED = "applied"; +const STATE_APPLIED_SVC = "applied-service"; +const STATE_SUCCEEDED = "succeeded"; +const STATE_DOWNLOAD_FAILED = "download-failed"; +const STATE_FAILED = "failed"; + +const LOADSOURCE_ERROR_WRONG_SIZE = 2; +const CRC_ERROR = 4; +const READ_ERROR = 6; +const WRITE_ERROR = 7; +const MAR_CHANNEL_MISMATCH_ERROR = 22; +const VERSION_DOWNGRADE_ERROR = 23; +const UPDATE_SETTINGS_FILE_CHANNEL = 38; +const SERVICE_COULD_NOT_COPY_UPDATER = 49; +const SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR = 52; +const SERVICE_INVALID_APPLYTO_DIR_ERROR = 54; +const SERVICE_INVALID_INSTALL_DIR_PATH_ERROR = 55; +const SERVICE_INVALID_WORKING_DIR_PATH_ERROR = 56; +const INVALID_APPLYTO_DIR_STAGED_ERROR = 72; +const INVALID_APPLYTO_DIR_ERROR = 74; +const INVALID_INSTALL_DIR_PATH_ERROR = 75; +const INVALID_WORKING_DIR_PATH_ERROR = 76; +const INVALID_CALLBACK_PATH_ERROR = 77; +const INVALID_CALLBACK_DIR_ERROR = 78; + +// Error codes 80 through 99 are reserved for nsUpdateService.js and are not +// defined in common/updatererrors.h +const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; +const ERR_UPDATE_STATE_NONE = 91; +const ERR_CHANNEL_CHANGE = 92; + +const WRITE_ERROR_BACKGROUND_TASK_SHARING_VIOLATION = 106; + +const STATE_FAILED_DELIMETER = ": "; + +const STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE = + STATE_FAILED + STATE_FAILED_DELIMETER + LOADSOURCE_ERROR_WRONG_SIZE; +const STATE_FAILED_CRC_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + CRC_ERROR; +const STATE_FAILED_READ_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + READ_ERROR; +const STATE_FAILED_WRITE_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + WRITE_ERROR; +const STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + MAR_CHANNEL_MISMATCH_ERROR; +const STATE_FAILED_VERSION_DOWNGRADE_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + VERSION_DOWNGRADE_ERROR; +const STATE_FAILED_UPDATE_SETTINGS_FILE_CHANNEL = + STATE_FAILED + STATE_FAILED_DELIMETER + UPDATE_SETTINGS_FILE_CHANNEL; +const STATE_FAILED_SERVICE_COULD_NOT_COPY_UPDATER = + STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_COULD_NOT_COPY_UPDATER; +const STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR = + STATE_FAILED + + STATE_FAILED_DELIMETER + + SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR; +const STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_INVALID_APPLYTO_DIR_ERROR; +const STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR = + STATE_FAILED + + STATE_FAILED_DELIMETER + + SERVICE_INVALID_INSTALL_DIR_PATH_ERROR; +const STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR = + STATE_FAILED + + STATE_FAILED_DELIMETER + + SERVICE_INVALID_WORKING_DIR_PATH_ERROR; +const STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_STAGED_ERROR; +const STATE_FAILED_INVALID_APPLYTO_DIR_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_ERROR; +const STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_INSTALL_DIR_PATH_ERROR; +const STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_WORKING_DIR_PATH_ERROR; +const STATE_FAILED_INVALID_CALLBACK_PATH_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_CALLBACK_PATH_ERROR; +const STATE_FAILED_INVALID_CALLBACK_DIR_ERROR = + STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_CALLBACK_DIR_ERROR; +const STATE_FAILED_WRITE_ERROR_BACKGROUND_TASK_SHARING_VIOLATION = + STATE_FAILED + + STATE_FAILED_DELIMETER + + WRITE_ERROR_BACKGROUND_TASK_SHARING_VIOLATION; + +const DEFAULT_UPDATE_VERSION = "999999.0"; + +/** + * Constructs a string representing a remote update xml file. + * + * @param aUpdates + * The string representing the update elements. + * @return The string representing a remote update xml file. + */ +function getRemoteUpdatesXMLString(aUpdates) { + return '<?xml version="1.0"?><updates>' + aUpdates + "</updates>"; +} + +/** + * Constructs a string representing an update element for a remote update xml + * file. See getUpdateString for parameter information not provided below. + * + * @param aUpdateProps + * An object containing non default test values for an nsIUpdate. + * See updateProps names below for possible object names. + * @param aPatches + * String representing the application update patches. + * @return The string representing an update element for an update xml file. + */ +function getRemoteUpdateString(aUpdateProps, aPatches) { + const updateProps = { + appVersion: DEFAULT_UPDATE_VERSION, + buildID: "20080811053724", + custom1: null, + custom2: null, + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, + name: "App Update Test", + promptWaitTime: null, + type: "major", + }; + + for (let name in aUpdateProps) { + updateProps[name] = aUpdateProps[name]; + } + + // To test that text nodes are handled properly the string returned contains + // spaces and newlines. + return getUpdateString(updateProps) + ">\n " + aPatches + "\n</update>\n"; +} + +/** + * Constructs a string representing a patch element for a remote update xml + * file. See getPatchString for parameter information not provided below. + * + * @param aPatchProps (optional) + * An object containing non default test values for an nsIUpdatePatch. + * See patchProps below for possible object names. + * @return The string representing a patch element for a remote update xml file. + */ +function getRemotePatchString(aPatchProps) { + const patchProps = { + type: "complete", + _url: null, + get url() { + if (this._url) { + return this._url; + } + if (gURLData) { + return gURLData + FILE_SIMPLE_MAR; + } + return null; + }, + set url(val) { + this._url = val; + }, + custom1: null, + custom2: null, + size: SIZE_SIMPLE_MAR, + }; + + for (let name in aPatchProps) { + patchProps[name] = aPatchProps[name]; + } + + return getPatchString(patchProps) + "/>"; +} + +/** + * Constructs a string representing a local update xml file. + * + * @param aUpdates + * The string representing the update elements. + * @return The string representing a local update xml file. + */ +function getLocalUpdatesXMLString(aUpdates) { + if (!aUpdates || aUpdates == "") { + return '<updates xmlns="http://www.mozilla.org/2005/app-update"/>'; + } + return ( + '<updates xmlns="http://www.mozilla.org/2005/app-update">' + + aUpdates + + "</updates>" + ); +} + +/** + * Constructs a string representing an update element for a local update xml + * file. See getUpdateString for parameter information not provided below. + * + * @param aUpdateProps + * An object containing non default test values for an nsIUpdate. + * See updateProps names below for possible object names. + * @param aPatches + * String representing the application update patches. + * @return The string representing an update element for an update xml file. + */ +function getLocalUpdateString(aUpdateProps, aPatches) { + const updateProps = { + _appVersion: null, + get appVersion() { + if (this._appVersion) { + return this._appVersion; + } + if (Services && Services.appinfo && Services.appinfo.version) { + return Services.appinfo.version; + } + return DEFAULT_UPDATE_VERSION; + }, + set appVersion(val) { + this._appVersion = val; + }, + buildID: "20080811053724", + channel: UpdateUtils ? UpdateUtils.getUpdateChannel() : "default", + custom1: null, + custom2: null, + detailsURL: URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS", + displayVersion: null, + foregroundDownload: "true", + installDate: "1238441400314", + isCompleteUpdate: "true", + name: "App Update Test", + previousAppVersion: null, + promptWaitTime: null, + serviceURL: "http://test_service/", + statusText: "Install Pending", + type: "major", + }; + + for (let name in aUpdateProps) { + updateProps[name] = aUpdateProps[name]; + } + + let checkInterval = updateProps.checkInterval + ? 'checkInterval="' + updateProps.checkInterval + '" ' + : ""; + let channel = 'channel="' + updateProps.channel + '" '; + let isCompleteUpdate = + 'isCompleteUpdate="' + updateProps.isCompleteUpdate + '" '; + let foregroundDownload = updateProps.foregroundDownload + ? 'foregroundDownload="' + updateProps.foregroundDownload + '" ' + : ""; + let installDate = 'installDate="' + updateProps.installDate + '" '; + let previousAppVersion = updateProps.previousAppVersion + ? 'previousAppVersion="' + updateProps.previousAppVersion + '" ' + : ""; + let statusText = updateProps.statusText + ? 'statusText="' + updateProps.statusText + '" ' + : ""; + let serviceURL = 'serviceURL="' + updateProps.serviceURL + '">'; + + return ( + getUpdateString(updateProps) + + " " + + checkInterval + + channel + + isCompleteUpdate + + foregroundDownload + + installDate + + previousAppVersion + + statusText + + serviceURL + + aPatches + + "</update>" + ); +} + +/** + * Constructs a string representing a patch element for a local update xml file. + * See getPatchString for parameter information not provided below. + * + * @param aPatchProps (optional) + * An object containing non default test values for an nsIUpdatePatch. + * See patchProps below for possible object names. + * @return The string representing a patch element for a local update xml file. + */ +function getLocalPatchString(aPatchProps) { + const patchProps = { + type: "complete", + url: gURLData + FILE_SIMPLE_MAR, + size: SIZE_SIMPLE_MAR, + custom1: null, + custom2: null, + selected: "true", + state: STATE_SUCCEEDED, + }; + + for (let name in aPatchProps) { + patchProps[name] = aPatchProps[name]; + } + + let selected = 'selected="' + patchProps.selected + '" '; + let state = 'state="' + patchProps.state + '"/>'; + return getPatchString(patchProps) + " " + selected + state; +} + +/** + * Constructs a string representing an update element for a remote update xml + * file. + * + * @param aUpdateProps (optional) + * An object containing non default test values for an nsIUpdate. + * See the aUpdateProps property names below for possible object names. + * @return The string representing an update element for an update xml file. + */ +function getUpdateString(aUpdateProps) { + let type = 'type="' + aUpdateProps.type + '" '; + let name = 'name="' + aUpdateProps.name + '" '; + let displayVersion = aUpdateProps.displayVersion + ? 'displayVersion="' + aUpdateProps.displayVersion + '" ' + : ""; + let appVersion = 'appVersion="' + aUpdateProps.appVersion + '" '; + // Not specifying a detailsURL will cause a leak due to bug 470244 + let detailsURL = 'detailsURL="' + aUpdateProps.detailsURL + '" '; + let promptWaitTime = aUpdateProps.promptWaitTime + ? 'promptWaitTime="' + aUpdateProps.promptWaitTime + '" ' + : ""; + let disableBITS = aUpdateProps.disableBITS + ? 'disableBITS="' + aUpdateProps.disableBITS + '" ' + : ""; + let disableBackgroundUpdates = aUpdateProps.disableBackgroundUpdates + ? 'disableBackgroundUpdates="' + + aUpdateProps.disableBackgroundUpdates + + '" ' + : ""; + let custom1 = aUpdateProps.custom1 ? aUpdateProps.custom1 + " " : ""; + let custom2 = aUpdateProps.custom2 ? aUpdateProps.custom2 + " " : ""; + let buildID = 'buildID="' + aUpdateProps.buildID + '"'; + + return ( + "<update " + + type + + name + + displayVersion + + appVersion + + detailsURL + + promptWaitTime + + disableBITS + + disableBackgroundUpdates + + custom1 + + custom2 + + buildID + ); +} + +/** + * Constructs a string representing a patch element for an update xml file. + * + * @param aPatchProps (optional) + * An object containing non default test values for an nsIUpdatePatch. + * See the patchProps property names below for possible object names. + * @return The string representing a patch element for an update xml file. + */ +function getPatchString(aPatchProps) { + let type = 'type="' + aPatchProps.type + '" '; + let url = 'URL="' + aPatchProps.url + '" '; + let size = 'size="' + aPatchProps.size + '"'; + let custom1 = aPatchProps.custom1 ? aPatchProps.custom1 + " " : ""; + let custom2 = aPatchProps.custom2 ? aPatchProps.custom2 + " " : ""; + return "<patch " + type + url + custom1 + custom2 + size; +} + +/** + * Reads the binary contents of a file and returns it as a string. + * + * @param aFile + * The file to read from. + * @return The contents of the file as a string. + */ +function readFileBytes(aFile) { + let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + // Specifying -1 for ioFlags will open the file with the default of PR_RDONLY. + // Specifying -1 for perm will open the file with the default of 0. + fis.init(aFile, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF); + let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance( + Ci.nsIBinaryInputStream + ); + bis.setInputStream(fis); + let data = []; + let count = fis.available(); + while (count > 0) { + let bytes = bis.readByteArray(Math.min(65535, count)); + data.push(String.fromCharCode.apply(null, bytes)); + count -= bytes.length; + if (!bytes.length) { + throw new Error("Nothing read from input stream!"); + } + } + data = data.join(""); + fis.close(); + return data.toString(); +} diff --git a/toolkit/mozapps/update/tests/data/simple.mar b/toolkit/mozapps/update/tests/data/simple.mar Binary files differnew file mode 100644 index 0000000000..fd635b46bd --- /dev/null +++ b/toolkit/mozapps/update/tests/data/simple.mar diff --git a/toolkit/mozapps/update/tests/data/syncManagerTestChild.js b/toolkit/mozapps/update/tests/data/syncManagerTestChild.js new file mode 100644 index 0000000000..1f52554e7c --- /dev/null +++ b/toolkit/mozapps/update/tests/data/syncManagerTestChild.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This is the script that runs in the child xpcshell process for the test +// unit_aus_update/updateSyncManager.js. +// The main thing this script does is override the child's directory service +// so that it ends up with the same fake binary path that the parent test runner +// has opened its update lock with. +// This requires that we have already been passed a constant on our command +// line which contains the relevant fake binary path, which is called: +/* global customExePath */ + +print("child process is running"); + +// This function is copied from xpcshellUtilsAUS.js so that we can have our +// xpcshell subprocess call it without having to load that whole file, because +// it turns out that needs a bunch of infrastructure that normally the testing +// framework would provide, and that also requires a bunch of setup, and it's +// just not worth all that. This is a cut down version that only includes the +// directory provider functionality that the subprocess really needs. +function adjustGeneralPaths() { + let dirProvider = { + getFile: function AGP_DP_getFile(aProp, aPersistent) { + // Set the value of persistent to false so when this directory provider is + // unregistered it will revert back to the original provider. + aPersistent.value = false; + // The sync manager only needs XREExeF, so that's all we provide. + if (aProp == "XREExeF") { + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + file.initWithPath(customExePath); + return file; + } + return null; + }, + QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]), + }; + let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService); + ds.QueryInterface(Ci.nsIProperties).undefine("XREExeF"); + ds.registerProvider(dirProvider); + + // Now that we've overridden the directory provider, the name of the update + // lock needs to be changed to match the overridden path. + let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( + Ci.nsIUpdateSyncManager + ); + syncManager.resetLock(); +} + +adjustGeneralPaths(); + +// Wait a few seconds for the parent to do what it needs to do, then exit. +print("child process should now have the lock; will exit in 5 seconds"); +simulateNoScriptActivity(5); +print("child process exiting now"); diff --git a/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js b/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js new file mode 100644 index 0000000000..90880d96d9 --- /dev/null +++ b/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js @@ -0,0 +1,29 @@ +/* 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/. */ + +#filter substitution + +/* Preprocessed constants used by xpcshell tests */ + +// MOZ_APP_VENDOR is optional. +#ifdef MOZ_APP_VENDOR +const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@"; +#else +const MOZ_APP_VENDOR = ""; +#endif + +// MOZ_APP_BASENAME is not optional for tests. +const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@"; + +#ifdef MOZ_VERIFY_MAR_SIGNATURE +const MOZ_VERIFY_MAR_SIGNATURE = true; +#else +const MOZ_VERIFY_MAR_SIGNATURE = false; +#endif + +#ifdef DISABLE_UPDATER_AUTHENTICODE_CHECK + const IS_AUTHENTICODE_CHECK_ENABLED = false; +#else + const IS_AUTHENTICODE_CHECK_ENABLED = true; +#endif diff --git a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js new file mode 100644 index 0000000000..111f0b1e8c --- /dev/null +++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js @@ -0,0 +1,4803 @@ +/* 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 { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.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"); + +const DIR_MACOS = AppConstants.platform == "macosx" ? "Contents/MacOS/" : ""; +const DIR_RESOURCES = + AppConstants.platform == "macosx" ? "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 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 = "TestAUSHelper" + mozinfo.bin_suffix; +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" + 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"; + +// 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; + +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 gCallbackBinFile = "callback_app" + mozinfo.bin_suffix; +var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"]; +var gPostUpdateBinFile = "postup_app" + mozinfo.bin_suffix; + +var gTimeoutRuns = 0; + +// Environment related globals +var gShouldResetEnv = undefined; +var gAddedEnvXRENoWindowsCrashDialog = false; +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 gTestFiles = []; +var gTestDirs = []; + +// Common files for both successful and failed updates. +var gTestFilesCommon = [ + { + 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, + }, + { + description: "Should never change", + fileName: "channel-prefs.js", + relPathDir: DIR_RESOURCES + "defaults/pref/", + originalContents: "ShouldNotBeReplaced\n", + compareContents: "ShouldNotBeReplaced\n", + originalFile: null, + compareFile: null, + originalPerms: 0o767, + comparePerms: 0o767, + }, +]; + +// 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); + +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"); + let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( + Ci.nsIUpdateSyncManager + ); + syncManager.resetLock(); + + // Remove the updates directory on Windows and Mac OS X which is located + // outside of the application directory after the call to adjustGeneralPaths + // has set it up. Since the test hasn't ran yet and the directory shouldn't + // exist this is 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; +} + +/** + * 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 == "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) {} + } + } + } + + 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) {} + } + + // 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 + ); + } + } + + 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. + */ +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); + } + + 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. + */ +function checkUpdateManager( + aStatusFileState, + aHasActiveUpdate, + aUpdateStatusState, + aUpdateErrCode, + aUpdateCount +) { + let activeUpdate = + aUpdateStatusState == STATE_DOWNLOADING + ? gUpdateManager.downloadingUpdate + : gUpdateManager.readyUpdate; + 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" + ); + } + Assert.equal( + gUpdateManager.getUpdateCount(), + aUpdateCount, + msgTags[i] + "the update manager updateCount attribute" + MSG_SHOULD_EQUAL + ); + if (aUpdateCount > 0) { + let update = gUpdateManager.getUpdateAt(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. + */ +function standardInit() { + // Initialize the update service stub component + 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 Mac OS X 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; +} + +XPCOMUtils.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; +}); + +XPCOMUtils.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); +}); + +XPCOMUtils.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); +}); + +XPCOMUtils.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"); + } +} + +XPCOMUtils.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 appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).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)); +} + +/** + * 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 updaterLeafName = + AppConstants.platform == "macosx" ? "updater.app" : FILE_UPDATER_BIN; + let testUpdater = getTestDirFile(updaterLeafName); + let updater = getGREBinDir(); + updater.append(updaterLeafName); + if (!updater.exists()) { + testUpdater.copyToFollowingLinks(updater.parent, updaterLeafName); + } + if (AppConstants.platform == "macosx") { + 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 + * The expected value of update.status when the update finishes. For + * service tests passing STATE_PENDING or STATE_APPLIED will change the + * value to STATE_PENDING_SVC and STATE_APPLIED_SVC respectively. + * @param aSwitchApp + * If true the update should switch the application with an updated + * staged application and if false the update should be applied to the + * installed application. + * @param aExpectedExitValue + * The expected exit value from the updater binary for non-service + * tests. + * @param aCheckSvcLog + * Whether the service log should be checked for service tests. + * @param aPatchDirPath (optional) + * When specified the patch directory path to use for invalid argument + * tests otherwise the normal path will be used. + * @param aInstallDirPath (optional) + * When specified the install directory path to use for invalid + * argument tests otherwise the normal path will be used. + * @param aApplyToDirPath (optional) + * When specified the apply to / working directory path to use for + * invalid argument tests otherwise the normal path will be used. + * @param aCallbackPath (optional) + * When specified the callback path to use for invalid argument tests + * otherwise the normal path will be used. + */ +function runUpdate( + aExpectedStatus, + aSwitchApp, + aExpectedExitValue, + aCheckSvcLog, + aPatchDirPath, + aInstallDirPath, + aApplyToDirPath, + aCallbackPath +) { + let isInvalidArgTest = + !!aPatchDirPath || + !!aInstallDirPath || + !!aApplyToDirPath || + !!aCallbackPath; + + let svcOriginalLog; + if (gIsServiceTest) { + copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN, false); + copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, false); + if (aCheckSvcLog) { + svcOriginalLog = readServiceLogFile(); + } + } + + let pid = 0; + if (gPIDPersistProcess) { + pid = gPIDPersistProcess.pid; + Services.env.set("MOZ_TEST_SHORTER_WAIT_PID", "1"); + } + + let updateBin = copyTestUpdaterToBinDir(); + Assert.ok(updateBin.exists(), MSG_SHOULD_EXIST + getMsgPath(updateBin.path)); + + let updatesDirPath = aPatchDirPath || getUpdateDirFile(DIR_PATCH).path; + let installDirPath = aInstallDirPath || getApplyDirFile().path; + let applyToDirPath = aApplyToDirPath || getApplyDirFile().path; + let stageDirPath = aApplyToDirPath || getStageDirFile().path; + + let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile); + Assert.ok( + callbackApp.exists(), + MSG_SHOULD_EXIST + ", path: " + callbackApp.path + ); + callbackApp.permissions = PERMS_DIRECTORY; + + setAppBundleModTime(); + + let args = [updatesDirPath, installDirPath]; + if (aSwitchApp) { + args[2] = stageDirPath; + args[3] = pid + "/replace"; + } else { + args[2] = applyToDirPath; + args[3] = pid; + } + + let launchBin = gIsServiceTest && isInvalidArgTest ? callbackApp : updateBin; + + if (!isInvalidArgTest) { + args = args.concat([callbackApp.parent.path, callbackApp.path]); + args = args.concat(gCallbackArgs); + } else if (gIsServiceTest) { + args = ["launch-service", updateBin.path].concat(args); + } else if (aCallbackPath) { + args = args.concat([callbackApp.parent.path, aCallbackPath]); + } + + debugDump("launching the program: " + launchBin.path + " " + args.join(" ")); + + if (aSwitchApp && !isInvalidArgTest) { + // We want to set the env vars again + gShouldResetEnv = undefined; + } + + setEnvironment(); + + let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + process.init(launchBin); + process.run(true, args, args.length); + + resetEnvironment(); + + if (gPIDPersistProcess) { + Services.env.set("MOZ_TEST_SHORTER_WAIT_PID", ""); + } + + let status = readStatusFile(); + if ( + (!gIsServiceTest && process.exitValue != aExpectedExitValue) || + (status != aExpectedStatus && !gIsServiceTest && !isInvalidArgTest) + ) { + if (process.exitValue != aExpectedExitValue) { + logTestInfo( + "updater exited with unexpected value! Got: " + + process.exitValue + + ", Expected: " + + aExpectedExitValue + ); + } + if (status != aExpectedStatus) { + logTestInfo( + "update status is not the expected status! Got: " + + status + + ", Expected: " + + aExpectedStatus + ); + } + logUpdateLog(FILE_LAST_UPDATE_LOG); + } + + if (gIsServiceTest && isInvalidArgTest) { + let secureStatus = readSecureStatusFile(); + if (secureStatus != STATE_NONE) { + status = secureStatus; + } + } + + if (!gIsServiceTest) { + Assert.equal( + process.exitValue, + aExpectedExitValue, + "the process exit value" + MSG_SHOULD_EQUAL + ); + } + + if (status != aExpectedStatus) { + logUpdateLog(FILE_UPDATE_LOG); + } + Assert.equal(status, aExpectedStatus, "the update status" + MSG_SHOULD_EQUAL); + + Assert.ok( + !updateHasBinaryTransparencyErrorResult(), + "binary transparency is not being processed for now" + ); + + if (gIsServiceTest && aCheckSvcLog) { + let contents = readServiceLogFile(); + Assert.notEqual( + contents, + svcOriginalLog, + "the contents of the maintenanceservice.log should not " + + "be the same as the original contents" + ); + if (gEnvForceServiceFallback) { + // If we are forcing the service to fail and fall back to update without + // the service, the service log should reflect that we failed in that way. + Assert.ok( + contents.includes(LOG_SVC_UNSUCCESSFUL_LAUNCH), + "the contents of the maintenanceservice.log should " + + "contain the unsuccessful launch string" + ); + } else if (!isInvalidArgTest) { + Assert.notEqual( + contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), + -1, + "the contents of the maintenanceservice.log should " + + "contain the successful launch string" + ); + } + } +} + +/** + * Launches the helper binary synchronously with the specified arguments for + * updater tests. + * + * @param aArgs + * The arguments to pass to the helper binary. + * @return the process exit value returned by the helper binary. + */ +function runTestHelperSync(aArgs) { + let helperBin = getTestDirFile(FILE_HELPER_BIN); + let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + process.init(helperBin); + debugDump("Running " + helperBin.path + " " + aArgs.join(" ")); + process.run(true, aArgs, aArgs.length); + return process.exitValue; +} + +/** + * Creates a symlink for updater tests. + */ +function createSymlink() { + let args = [ + "setup-symlink", + "moz-foo", + "moz-bar", + "target", + getApplyDirFile().path + "/" + DIR_RESOURCES + "link", + ]; + let exitValue = runTestHelperSync(args); + Assert.equal(exitValue, 0, "the helper process exit value should be 0"); + let file = getApplyDirFile(DIR_RESOURCES + "link"); + Assert.ok(file.exists(), MSG_SHOULD_EXIST + ", path: " + file.path); + file.permissions = 0o666; + args = [ + "setup-symlink", + "moz-foo2", + "moz-bar2", + "target2", + getApplyDirFile().path + "/" + DIR_RESOURCES + "link2", + "change-perm", + ]; + exitValue = runTestHelperSync(args); + Assert.equal(exitValue, 0, "the helper process exit value should be 0"); +} + +/** + * Removes a symlink for updater tests. + */ +function removeSymlink() { + let args = [ + "remove-symlink", + "moz-foo", + "moz-bar", + "target", + getApplyDirFile().path + "/" + DIR_RESOURCES + "link", + ]; + let exitValue = runTestHelperSync(args); + Assert.equal(exitValue, 0, "the helper process exit value should be 0"); + args = [ + "remove-symlink", + "moz-foo2", + "moz-bar2", + "target2", + getApplyDirFile().path + "/" + DIR_RESOURCES + "link2", + ]; + exitValue = runTestHelperSync(args); + Assert.equal(exitValue, 0, "the helper process exit value should be 0"); +} + +/** + * Checks a symlink for updater tests. + */ +function checkSymlink() { + let args = [ + "check-symlink", + getApplyDirFile().path + "/" + DIR_RESOURCES + "link", + ]; + let exitValue = runTestHelperSync(args); + Assert.equal(exitValue, 0, "the helper process exit value should be 0"); +} + +/** + * Sets the active update and related information for updater tests. + */ +function setupActiveUpdate() { + let pendingState = gIsServiceTest ? STATE_PENDING_SVC : STATE_PENDING; + let patchProps = { state: pendingState }; + let patches = getLocalPatchString(patchProps); + let updates = getLocalUpdateString({}, patches); + writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true); + writeVersionFile(DEFAULT_UPDATE_VERSION); + writeStatusFile(pendingState); + reloadUpdateManagerData(); + Assert.ok(!!gUpdateManager.readyUpdate, "the ready update should be defined"); +} + +/** + * Stages an update using nsIUpdateProcessor:processUpdate for updater tests. + * + * @param aStateAfterStage + * The expected update state after the update has been staged. + * @param aCheckSvcLog + * Whether the service log should be checked for service tests. + * @param aUpdateRemoved (optional) + * Whether the update is removed after staging. This can happen when + * a staging failure occurs. + */ +async function stageUpdate( + aStateAfterStage, + aCheckSvcLog, + aUpdateRemoved = false +) { + debugDump("start - attempting to stage update"); + + let svcLogOriginalContents; + if (gIsServiceTest && aCheckSvcLog) { + svcLogOriginalContents = readServiceLogFile(); + } + + setAppBundleModTime(); + setEnvironment(); + try { + // Stage the update. + Cc["@mozilla.org/updates/update-processor;1"] + .createInstance(Ci.nsIUpdateProcessor) + .processUpdate(); + } catch (e) { + Assert.ok( + false, + "error thrown while calling processUpdate, Exception: " + e + ); + } + await waitForEvent("update-staged", aStateAfterStage); + resetEnvironment(); + + if (AppConstants.platform == "win") { + if (gIsServiceTest) { + waitForServiceStop(false); + } else { + let updater = getApplyDirFile(FILE_UPDATER_BIN); + await TestUtils.waitForCondition( + () => !isFileInUse(updater), + "Waiting for the file tp not be in use, Path: " + updater.path + ); + } + } + + if (!aUpdateRemoved) { + Assert.equal( + readStatusState(), + aStateAfterStage, + "the status file state" + MSG_SHOULD_EQUAL + ); + + Assert.equal( + gUpdateManager.readyUpdate.state, + aStateAfterStage, + "the update state" + MSG_SHOULD_EQUAL + ); + } + + let log = getUpdateDirFile(FILE_LAST_UPDATE_LOG); + Assert.ok(log.exists(), MSG_SHOULD_EXIST + getMsgPath(log.path)); + + log = getUpdateDirFile(FILE_UPDATE_LOG); + Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST + getMsgPath(log.path)); + + log = getUpdateDirFile(FILE_BACKUP_UPDATE_LOG); + Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST + getMsgPath(log.path)); + + let stageDir = getStageDirFile(); + if ( + aStateAfterStage == STATE_APPLIED || + aStateAfterStage == STATE_APPLIED_SVC + ) { + Assert.ok(stageDir.exists(), MSG_SHOULD_EXIST + getMsgPath(stageDir.path)); + } else { + Assert.ok( + !stageDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(stageDir.path) + ); + } + + if (gIsServiceTest && aCheckSvcLog) { + let contents = readServiceLogFile(); + Assert.notEqual( + contents, + svcLogOriginalContents, + "the contents of the maintenanceservice.log should not " + + "be the same as the original contents" + ); + Assert.notEqual( + contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), + -1, + "the contents of the maintenanceservice.log should " + + "contain the successful launch string" + ); + } + + debugDump("finish - attempting to stage update"); +} + +/** + * Helper function to check whether the maintenance service updater tests should + * run. See bug 711660 for more details. + * + * @return true if the test should run and false if it shouldn't. + * @throws If called from a platform other than Windows. + */ +function shouldRunServiceTest() { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + let binDir = getGREBinDir(); + let updaterBin = binDir.clone(); + updaterBin.append(FILE_UPDATER_BIN); + Assert.ok( + updaterBin.exists(), + MSG_SHOULD_EXIST + ", leafName: " + updaterBin.leafName + ); + + let updaterBinPath = updaterBin.path; + if (/ /.test(updaterBinPath)) { + updaterBinPath = '"' + updaterBinPath + '"'; + } + + let isBinSigned = isBinarySigned(updaterBinPath); + + const REG_PATH = + "SOFTWARE\\Mozilla\\MaintenanceService\\" + + "3932ecacee736d366d6436db0f55bce4"; + 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_READ | key.WOW64_64 + ); + } catch (e) { + // The build system could sign the files and not have the test registry key + // in which case we should fail the test if the updater binary is signed so + // the build system can be fixed by adding the registry key. + if (IS_AUTHENTICODE_CHECK_ENABLED) { + Assert.ok( + !isBinSigned, + "the updater.exe binary should not be signed when the test " + + "registry key doesn't exist (if it is, build system " + + "configuration bug?)" + ); + } + + logTestInfo( + "this test can only run on the buildbot build system at this time" + ); + return false; + } + + // Check to make sure the service is installed + let args = ["wait-for-service-stop", "MozillaMaintenance", "10"]; + let exitValue = runTestHelperSync(args); + Assert.notEqual( + exitValue, + 0xee, + "the maintenance service should be " + + "installed (if not, build system configuration bug?)" + ); + + if (IS_AUTHENTICODE_CHECK_ENABLED) { + // The test registry key exists and IS_AUTHENTICODE_CHECK_ENABLED is true + // so the binaries should be signed. To run the test locally + // DISABLE_UPDATER_AUTHENTICODE_CHECK can be defined. + Assert.ok( + isBinSigned, + "the updater.exe binary should be signed (if not, build system " + + "configuration bug?)" + ); + } + + // In case the machine is running an old maintenance service or if it + // is not installed, and permissions exist to install it. Then install + // the newer bin that we have since all of the other checks passed. + return attemptServiceInstall(); +} + +/** + * Helper function to check whether the a binary is signed. + * + * @param aBinPath + * The path to the file to check if it is signed. + * @return true if the file is signed and false if it isn't. + */ +function isBinarySigned(aBinPath) { + let args = ["check-signature", aBinPath]; + let exitValue = runTestHelperSync(args); + if (exitValue != 0) { + logTestInfo( + "binary is not signed. " + + FILE_HELPER_BIN + + " returned " + + exitValue + + " for file " + + aBinPath + ); + return false; + } + return true; +} + +/** + * Helper function for setting up the application files required to launch the + * application for the updater tests by either copying or creating symlinks to + * the files. + * + * @param options.requiresOmnijar when true, copy or symlink omnijars as well. + * This may be required to launch the updated application and have non-trivial + * functionality available. + */ +function setupAppFiles({ requiresOmnijar = false } = {}) { + debugDump( + "start - copying or creating symlinks to application files " + + "for the test" + ); + + let destDir = getApplyDirFile(); + if (!destDir.exists()) { + try { + destDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + } catch (e) { + logTestInfo( + "unable to create directory! Path: " + + destDir.path + + ", Exception: " + + e + ); + do_throw(e); + } + } + + // Required files for the application or the test that aren't listed in the + // dependentlibs.list file. + let appFiles = [ + { relPath: FILE_APP_BIN, inGreDir: false }, + { relPath: FILE_APPLICATION_INI, inGreDir: true }, + { relPath: "dependentlibs.list", inGreDir: true }, + ]; + + if (requiresOmnijar) { + appFiles.push({ relPath: AppConstants.OMNIJAR_NAME, inGreDir: true }); + + if (AppConstants.MOZ_BUILD_APP == "browser") { + // Only Firefox uses an app-specific omnijar. + appFiles.push({ + relPath: "browser/" + AppConstants.OMNIJAR_NAME, + inGreDir: true, + }); + } + } + + // On Linux the updater.png must also be copied and libsoftokn3.so must be + // symlinked or copied. + if (AppConstants.platform == "linux") { + appFiles.push( + { relPath: "icons/updater.png", inGreDir: true }, + { relPath: "libsoftokn3.so", inGreDir: true } + ); + } + + // Read the dependent libs file leafnames from the dependentlibs.list file + // into the array. + let deplibsFile = gGREDirOrig.clone(); + deplibsFile.append("dependentlibs.list"); + let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( + Ci.nsIFileInputStream + ); + fis.init(deplibsFile, 0x01, 0o444, Ci.nsIFileInputStream.CLOSE_ON_EOF); + fis.QueryInterface(Ci.nsILineInputStream); + + let hasMore; + let line = {}; + do { + hasMore = fis.readLine(line); + appFiles.push({ relPath: line.value, inGreDir: false }); + } while (hasMore); + + fis.close(); + + appFiles.forEach(function CMAF_FLN_FE(aAppFile) { + copyFileToTestAppDir(aAppFile.relPath, aAppFile.inGreDir); + }); + + copyTestUpdaterToBinDir(); + + debugDump( + "finish - copying or creating symlinks to application files " + + "for the test" + ); +} + +/** + * Copies the specified files from the dist/bin directory into the test's + * application directory. + * + * @param aFileRelPath + * The relative path to the source and the destination of the file to + * copy. + * @param aInGreDir + * Whether the file is located in the GRE directory which is + * <bundle>/Contents/Resources on Mac OS X and is the installation + * directory on all other platforms. If false the file must be in the + * GRE Binary directory which is <bundle>/Contents/MacOS on Mac OS X + * and is the installation directory on on all other platforms. + */ +function copyFileToTestAppDir(aFileRelPath, aInGreDir) { + // gGREDirOrig and gGREBinDirOrig must always be cloned when changing its + // properties + let srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone(); + let destFile = aInGreDir ? getGREDir() : getGREBinDir(); + let fileRelPath = aFileRelPath; + let pathParts = fileRelPath.split("/"); + for (let i = 0; i < pathParts.length; i++) { + if (pathParts[i]) { + srcFile.append(pathParts[i]); + destFile.append(pathParts[i]); + } + } + + if (AppConstants.platform == "macosx" && !srcFile.exists()) { + debugDump( + "unable to copy file since it doesn't exist! Checking if " + + fileRelPath + + ".app exists. Path: " + + srcFile.path + ); + // gGREDirOrig and gGREBinDirOrig must always be cloned when changing its + // properties + srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone(); + destFile = aInGreDir ? getGREDir() : getGREBinDir(); + for (let i = 0; i < pathParts.length; i++) { + if (pathParts[i]) { + srcFile.append( + pathParts[i] + (pathParts.length - 1 == i ? ".app" : "") + ); + destFile.append( + pathParts[i] + (pathParts.length - 1 == i ? ".app" : "") + ); + } + } + fileRelPath = fileRelPath + ".app"; + } + Assert.ok( + srcFile.exists(), + MSG_SHOULD_EXIST + ", leafName: " + srcFile.leafName + ); + + // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a + // file extension and shouldSymlink will always be false on Windows. + let shouldSymlink = + pathParts[pathParts.length - 1] == "XUL" || + fileRelPath.substr(fileRelPath.length - 3) == ".so" || + fileRelPath.substr(fileRelPath.length - 6) == ".dylib"; + if (!shouldSymlink) { + if (!destFile.exists()) { + try { + srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName); + } catch (e) { + // Just in case it is partially copied + if (destFile.exists()) { + try { + destFile.remove(true); + } catch (ex) { + logTestInfo( + "unable to remove file that failed to copy! Path: " + + destFile.path + + ", Exception: " + + ex + ); + } + } + do_throw( + "Unable to copy file! Path: " + srcFile.path + ", Exception: " + e + ); + } + } + } else { + try { + if (destFile.exists()) { + destFile.remove(false); + } + let ln = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + ln.initWithPath("/bin/ln"); + let process = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + process.init(ln); + let args = ["-s", srcFile.path, destFile.path]; + process.run(true, args, args.length); + Assert.ok( + destFile.isSymlink(), + destFile.leafName + " should be a symlink" + ); + } catch (e) { + do_throw( + "Unable to create symlink for file! Path: " + + srcFile.path + + ", Exception: " + + e + ); + } + } +} + +/** + * Attempts to upgrade the maintenance service if permissions are allowed. + * This is useful for XP where we have permission to upgrade in case an + * older service installer exists. Also if the user manually installed into + * a unprivileged location. + * + * @return true if the installed service is from this build. If the installed + * service is not from this build the test will fail instead of + * returning false. + * @throws If called from a platform other than Windows. + */ +function attemptServiceInstall() { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + let maintSvcDir = getMaintSvcDir(); + Assert.ok( + maintSvcDir.exists(), + MSG_SHOULD_EXIST + ", leafName: " + maintSvcDir.leafName + ); + let oldMaintSvcBin = maintSvcDir.clone(); + oldMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN); + Assert.ok( + oldMaintSvcBin.exists(), + MSG_SHOULD_EXIST + ", leafName: " + oldMaintSvcBin.leafName + ); + let buildMaintSvcBin = getGREBinDir(); + buildMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN); + if (readFileBytes(oldMaintSvcBin) == readFileBytes(buildMaintSvcBin)) { + debugDump( + "installed maintenance service binary is the same as the " + + "build's maintenance service binary" + ); + return true; + } + let backupMaintSvcBin = maintSvcDir.clone(); + backupMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN + ".backup"); + try { + if (backupMaintSvcBin.exists()) { + backupMaintSvcBin.remove(false); + } + oldMaintSvcBin.moveTo( + maintSvcDir, + FILE_MAINTENANCE_SERVICE_BIN + ".backup" + ); + buildMaintSvcBin.copyTo(maintSvcDir, FILE_MAINTENANCE_SERVICE_BIN); + backupMaintSvcBin.remove(false); + } catch (e) { + // Restore the original file in case the moveTo was successful. + if (backupMaintSvcBin.exists()) { + oldMaintSvcBin = maintSvcDir.clone(); + oldMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN); + if (!oldMaintSvcBin.exists()) { + backupMaintSvcBin.moveTo(maintSvcDir, FILE_MAINTENANCE_SERVICE_BIN); + } + } + Assert.ok( + false, + "should be able copy the test maintenance service to " + + "the maintenance service directory (if not, build system " + + "configuration bug?), path: " + + maintSvcDir.path + ); + } + + return true; +} + +/** + * Waits for the applications that are launched by the maintenance service to + * stop. + * + * @throws If called from a platform other than Windows. + */ +function waitServiceApps() { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + // maintenanceservice_installer.exe is started async during updates. + waitForApplicationStop("maintenanceservice_installer.exe"); + // maintenanceservice_tmp.exe is started async from the service installer. + waitForApplicationStop("maintenanceservice_tmp.exe"); + // In case the SCM thinks the service is stopped, but process still exists. + waitForApplicationStop("maintenanceservice.exe"); +} + +/** + * Waits for the maintenance service to stop. + * + * @throws If called from a platform other than Windows. + */ +function waitForServiceStop(aFailTest) { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + waitServiceApps(); + debugDump("waiting for the maintenance service to stop if necessary"); + // Use the helper bin to ensure the service is stopped. If not stopped, then + // wait for the service to stop (at most 120 seconds). + let args = ["wait-for-service-stop", "MozillaMaintenance", "120"]; + let exitValue = runTestHelperSync(args); + Assert.notEqual(exitValue, 0xee, "the maintenance service should exist"); + if (exitValue != 0) { + if (aFailTest) { + Assert.ok( + false, + "the maintenance service should stop, process exit " + + "value: " + + exitValue + ); + } + logTestInfo( + "maintenance service did not stop which may cause test " + + "failures later, process exit value: " + + exitValue + ); + } else { + debugDump("service stopped"); + } + waitServiceApps(); +} + +/** + * Waits for the specified application to stop. + * + * @param aApplication + * The application binary name to wait until it has stopped. + * @throws If called from a platform other than Windows. + */ +function waitForApplicationStop(aApplication) { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + debugDump("waiting for " + aApplication + " to stop if necessary"); + // Use the helper bin to ensure the application is stopped. If not stopped, + // then wait for it to stop (at most 120 seconds). + let args = ["wait-for-application-exit", aApplication, "120"]; + let exitValue = runTestHelperSync(args); + Assert.equal( + exitValue, + 0, + "the process should have stopped, process name: " + aApplication + ); +} + +/** + * Gets the platform specific shell binary that is launched using nsIProcess and + * in turn launches a binary used for the test (e.g. application, updater, + * etc.). A shell is used so debug console output can be redirected to a file so + * it doesn't end up in the test log. + * + * @return nsIFile for the shell binary to launch using nsIProcess. + */ +function getLaunchBin() { + let launchBin; + if (AppConstants.platform == "win") { + launchBin = Services.dirsvc.get("WinD", Ci.nsIFile); + launchBin.append("System32"); + launchBin.append("cmd.exe"); + } else { + launchBin = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + launchBin.initWithPath("/bin/sh"); + } + Assert.ok(launchBin.exists(), MSG_SHOULD_EXIST + getMsgPath(launchBin.path)); + + return launchBin; +} + +/** + * Locks a Windows directory. + * + * @param aDirPath + * The test file object that describes the file to make in use. + * @throws If called from a platform other than Windows. + */ +function lockDirectory(aDirPath) { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + debugDump("start - locking installation directory"); + const LPCWSTR = ctypes.char16_t.ptr; + const DWORD = ctypes.uint32_t; + const LPVOID = ctypes.voidptr_t; + const GENERIC_READ = 0x80000000; + const FILE_SHARE_READ = 1; + const FILE_SHARE_WRITE = 2; + const OPEN_EXISTING = 3; + const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + const INVALID_HANDLE_VALUE = LPVOID(0xffffffff); + let kernel32 = ctypes.open("kernel32"); + let CreateFile = kernel32.declare( + "CreateFileW", + ctypes.winapi_abi, + LPVOID, + LPCWSTR, + DWORD, + DWORD, + LPVOID, + DWORD, + DWORD, + LPVOID + ); + gHandle = CreateFile( + aDirPath, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + LPVOID(0), + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + LPVOID(0) + ); + Assert.notEqual( + gHandle.toString(), + INVALID_HANDLE_VALUE.toString(), + "the handle should not equal INVALID_HANDLE_VALUE" + ); + kernel32.close(); + debugDump("finish - locking installation directory"); +} + +/** + * Launches the test helper binary to make it in use for updater tests. + * + * @param aRelPath + * The relative path in the apply to directory for the helper binary. + * @param aCopyTestHelper + * Whether to copy the test helper binary to the relative path in the + * apply to directory. + */ +async function runHelperFileInUse(aRelPath, aCopyTestHelper) { + debugDump("aRelPath: " + aRelPath); + // Launch an existing file so it is in use during the update. + let helperBin = getTestDirFile(FILE_HELPER_BIN); + let fileInUseBin = getApplyDirFile(aRelPath); + if (aCopyTestHelper) { + if (fileInUseBin.exists()) { + fileInUseBin.remove(false); + } + helperBin.copyTo(fileInUseBin.parent, fileInUseBin.leafName); + } + fileInUseBin.permissions = PERMS_DIRECTORY; + let args = [ + getApplyDirPath() + DIR_RESOURCES, + "input", + "output", + "-s", + HELPER_SLEEP_TIMEOUT, + ]; + let fileInUseProcess = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + fileInUseProcess.init(fileInUseBin); + fileInUseProcess.run(false, args, args.length); + + await waitForHelperSleep(); +} + +/** + * Launches the test helper binary to provide a pid that is in use for updater + * tests. + * + * @param aRelPath + * The relative path in the apply to directory for the helper binary. + * @param aCopyTestHelper + * Whether to copy the test helper binary to the relative path in the + * apply to directory. + */ +async function runHelperPIDPersists(aRelPath, aCopyTestHelper) { + debugDump("aRelPath: " + aRelPath); + // Launch an existing file so it is in use during the update. + let helperBin = getTestDirFile(FILE_HELPER_BIN); + let pidPersistsBin = getApplyDirFile(aRelPath); + if (aCopyTestHelper) { + if (pidPersistsBin.exists()) { + pidPersistsBin.remove(false); + } + helperBin.copyTo(pidPersistsBin.parent, pidPersistsBin.leafName); + } + pidPersistsBin.permissions = PERMS_DIRECTORY; + let args = [ + getApplyDirPath() + DIR_RESOURCES, + "input", + "output", + "-s", + HELPER_SLEEP_TIMEOUT, + ]; + gPIDPersistProcess = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + gPIDPersistProcess.init(pidPersistsBin); + gPIDPersistProcess.run(false, args, args.length); + + await waitForHelperSleep(); + await TestUtils.waitForCondition( + () => !!gPIDPersistProcess.pid, + "Waiting for the process pid" + ); +} + +/** + * Launches the test helper binary and locks a file specified on the command + * line for updater tests. + * + * @param aTestFile + * The test file object that describes the file to lock. + */ +async function runHelperLockFile(aTestFile) { + // Exclusively lock an existing file so it is in use during the update. + let helperBin = getTestDirFile(FILE_HELPER_BIN); + let helperDestDir = getApplyDirFile(DIR_RESOURCES); + helperBin.copyTo(helperDestDir, FILE_HELPER_BIN); + helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN); + // Strip off the first two directories so the path has to be from the helper's + // working directory. + let lockFileRelPath = aTestFile.relPathDir.split("/"); + if (AppConstants.platform == "macosx") { + lockFileRelPath = lockFileRelPath.slice(2); + } + lockFileRelPath = lockFileRelPath.join("/") + "/" + aTestFile.fileName; + let args = [ + getApplyDirPath() + DIR_RESOURCES, + "input", + "output", + "-s", + HELPER_SLEEP_TIMEOUT, + lockFileRelPath, + ]; + let helperProcess = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + helperProcess.init(helperBin); + helperProcess.run(false, args, args.length); + + await waitForHelperSleep(); +} + +/** + * Helper function that waits until the helper has completed its operations. + */ +async function waitForHelperSleep() { + // Give the lock file process time to lock the file before updating otherwise + // this test can fail intermittently on Windows debug builds. + let file = getApplyDirFile(DIR_RESOURCES + "output"); + await TestUtils.waitForCondition( + () => file.exists(), + "Waiting for file to exist, path: " + file.path + ); + + let expectedContents = "sleeping\n"; + await TestUtils.waitForCondition( + () => readFile(file) == expectedContents, + "Waiting for expected file contents: " + expectedContents + ); + + await TestUtils.waitForCondition(() => { + try { + file.remove(false); + } catch (e) { + debugDump( + "failed to remove file. Path: " + file.path + ", Exception: " + e + ); + } + return !file.exists(); + }, "Waiting for file to be removed, Path: " + file.path); +} + +/** + * Helper function to tell the helper to finish and exit its sleep state. + */ +async function waitForHelperExit() { + let file = getApplyDirFile(DIR_RESOURCES + "input"); + writeFile(file, "finish\n"); + + // Give the lock file process time to lock the file before updating otherwise + // this test can fail intermittently on Windows debug builds. + file = getApplyDirFile(DIR_RESOURCES + "output"); + await TestUtils.waitForCondition( + () => file.exists(), + "Waiting for file to exist, Path: " + file.path + ); + + let expectedContents = "finished\n"; + await TestUtils.waitForCondition( + () => readFile(file) == expectedContents, + "Waiting for expected file contents: " + expectedContents + ); + + // Give the lock file process time to unlock the file before deleting the + // input and output files. + await TestUtils.waitForCondition(() => { + try { + file.remove(false); + } catch (e) { + debugDump( + "failed to remove file. Path: " + file.path + ", Exception: " + e + ); + } + return !file.exists(); + }, "Waiting for file to be removed, Path: " + file.path); + + file = getApplyDirFile(DIR_RESOURCES + "input"); + await TestUtils.waitForCondition(() => { + try { + file.remove(false); + } catch (e) { + debugDump( + "failed to remove file. Path: " + file.path + ", Exception: " + e + ); + } + return !file.exists(); + }, "Waiting for file to be removed, Path: " + file.path); +} + +/** + * Helper function for updater binary tests that creates the files and + * directories used by the test. + * + * @param aMarFile + * The mar file for the update test. + * @param aPostUpdateAsync + * When null the updater.ini is not created otherwise this parameter + * is passed to createUpdaterINI. + * @param aPostUpdateExeRelPathPrefix + * When aPostUpdateAsync null this value is ignored otherwise it is + * passed to createUpdaterINI. + * @param aSetupActiveUpdate + * Whether to setup the active update. + * + * @param options.requiresOmnijar + * When true, copy or symlink omnijars as well. This may be required + * to launch the updated application and have non-trivial functionality + * available. + */ +async function setupUpdaterTest( + aMarFile, + aPostUpdateAsync, + aPostUpdateExeRelPathPrefix = "", + aSetupActiveUpdate = true, + { requiresOmnijar = false } = {} +) { + debugDump("start - updater test setup"); + let updatesPatchDir = getUpdateDirFile(DIR_PATCH); + if (!updatesPatchDir.exists()) { + updatesPatchDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + } + // Copy the mar that will be applied + let mar = getTestDirFile(aMarFile); + mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_MAR); + + let helperBin = getTestDirFile(FILE_HELPER_BIN); + helperBin.permissions = PERMS_DIRECTORY; + let afterApplyBinDir = getApplyDirFile(DIR_RESOURCES); + helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile); + helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile); + + gTestFiles.forEach(function SUT_TF_FE(aTestFile) { + debugDump("start - setup test file: " + aTestFile.fileName); + if (aTestFile.originalFile || aTestFile.originalContents) { + let testDir = getApplyDirFile(aTestFile.relPathDir); + // Somehow these create calls are failing with FILE_ALREADY_EXISTS even + // after checking .exists() first, so we just catch the exception. + try { + testDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + } catch (e) { + if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) { + throw e; + } + } + + let testFile; + if (aTestFile.originalFile) { + testFile = getTestDirFile(aTestFile.originalFile); + testFile.copyToFollowingLinks(testDir, aTestFile.fileName); + testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName); + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + ", path: " + testFile.path + ); + } else { + testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName); + writeFile(testFile, aTestFile.originalContents); + } + + // Skip these tests on Windows since chmod doesn't really set permissions + // on Windows. + if (AppConstants.platform != "win" && aTestFile.originalPerms) { + testFile.permissions = aTestFile.originalPerms; + // Store the actual permissions on the file for reference later after + // setting the permissions. + if (!aTestFile.comparePerms) { + aTestFile.comparePerms = testFile.permissions; + } + } + } + debugDump("finish - setup test file: " + aTestFile.fileName); + }); + + // Add the test directory that will be updated for a successful update or left + // in the initial state for a failed update. + gTestDirs.forEach(function SUT_TD_FE(aTestDir) { + debugDump("start - setup test directory: " + aTestDir.relPathDir); + let testDir = getApplyDirFile(aTestDir.relPathDir); + // Somehow these create calls are failing with FILE_ALREADY_EXISTS even + // after checking .exists() first, so we just catch the exception. + try { + testDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + } catch (e) { + if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) { + throw e; + } + } + + if (aTestDir.files) { + aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) { + let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile); + if (!testFile.exists()) { + testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); + } + }); + } + + if (aTestDir.subDirs) { + aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) { + let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir); + if (!testSubDir.exists()) { + testSubDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + } + + if (aTestDir.subDirFiles) { + aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) { + let testFile = getApplyDirFile( + aTestDir.relPathDir + aSubDir + aTestFile + ); + if (!testFile.exists()) { + testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); + } + }); + } + }); + } + debugDump("finish - setup test directory: " + aTestDir.relPathDir); + }); + + if (aSetupActiveUpdate) { + setupActiveUpdate(); + } + + if (aPostUpdateAsync !== null) { + createUpdaterINI(aPostUpdateAsync, aPostUpdateExeRelPathPrefix); + } + + await TestUtils.waitForCondition(() => { + try { + setupAppFiles({ requiresOmnijar }); + return true; + } catch (e) { + logTestInfo("exception when calling setupAppFiles, Exception: " + e); + } + return false; + }, "Waiting to setup app files"); + + debugDump("finish - updater test setup"); +} + +/** + * Helper function for updater binary tests that creates the updater.ini + * file. + * + * @param aIsExeAsync + * True or undefined if the post update process should be async. If + * undefined ExeAsync will not be added to the updater.ini file in + * order to test the default launch behavior which is async. + * @param aExeRelPathPrefix + * A string to prefix the ExeRelPath values in the updater.ini. + */ +function createUpdaterINI(aIsExeAsync, aExeRelPathPrefix) { + let exeArg = "ExeArg=post-update-async\n"; + let exeAsync = ""; + if (aIsExeAsync !== undefined) { + if (aIsExeAsync) { + exeAsync = "ExeAsync=true\n"; + } else { + exeArg = "ExeArg=post-update-sync\n"; + exeAsync = "ExeAsync=false\n"; + } + } + + if (AppConstants.platform == "win" && aExeRelPathPrefix) { + aExeRelPathPrefix = aExeRelPathPrefix.replace("/", "\\"); + } + + let exeRelPathMac = + "ExeRelPath=" + + aExeRelPathPrefix + + DIR_RESOURCES + + gPostUpdateBinFile + + "\n"; + let exeRelPathWin = + "ExeRelPath=" + aExeRelPathPrefix + gPostUpdateBinFile + "\n"; + let updaterIniContents = + "[Strings]\n" + + "Title=Update Test\n" + + "Info=Running update test " + + gTestID + + "\n\n" + + "[PostUpdateMac]\n" + + exeRelPathMac + + exeArg + + exeAsync + + "\n" + + "[PostUpdateWin]\n" + + exeRelPathWin + + exeArg + + exeAsync; + let updaterIni = getApplyDirFile(DIR_RESOURCES + FILE_UPDATER_INI); + writeFile(updaterIni, updaterIniContents); +} + +/** + * Gets the message log path used for assert checks to lessen the length printed + * to the log file. + * + * @param aPath + * The path to shorten for the log file. + * @return the message including the shortened path for the log file. + */ +function getMsgPath(aPath) { + return ", path: " + replaceLogPaths(aPath); +} + +/** + * Helper function that replaces the common part of paths in the update log's + * contents with <test_dir_path> for paths to the the test directory and + * <update_dir_path> for paths to the update directory. This is needed since + * Assert.equal will truncate what it prints to the xpcshell log file. + * + * @param aLogContents + * The update log file's contents. + * @return the log contents with the paths replaced. + */ +function replaceLogPaths(aLogContents) { + let logContents = aLogContents; + // Remove the majority of the path up to the test directory. This is needed + // since Assert.equal won't print long strings to the test logs. + let testDirPath = getApplyDirFile().parent.path; + if (AppConstants.platform == "win") { + // Replace \\ with \\\\ so the regexp works. + testDirPath = testDirPath.replace(/\\/g, "\\\\"); + } + logContents = logContents.replace( + new RegExp(testDirPath, "g"), + "<test_dir_path>/" + gTestID + ); + let updatesDirPath = getMockUpdRootD().path; + if (AppConstants.platform == "win") { + // Replace \\ with \\\\ so the regexp works. + updatesDirPath = updatesDirPath.replace(/\\/g, "\\\\"); + } + logContents = logContents.replace( + new RegExp(updatesDirPath, "g"), + "<update_dir_path>/" + gTestID + ); + if (AppConstants.platform == "win") { + // Replace \ with / + logContents = logContents.replace(/\\/g, "/"); + } + return logContents; +} + +/** + * Helper function that removes the timestamps in the update log + * + * @param aLogContents + * The update log file's contents. + * @return the log contents without timestamps + */ +function removeTimeStamps(aLogContents) { + return aLogContents.replace( + /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{4}: /gm, + "" + ); +} + +/** + * Helper function for updater binary tests for verifying the contents of the + * update log after a successful update. + * + * @param aCompareLogFile + * The log file to compare the update log with. + * @param aStaged + * If the update log file is for a staged update. + * @param aReplace + * If the update log file is for a replace update. + * @param aExcludeDistDir + * Removes lines containing the distribution directory from the log + * file to compare the update log with. + */ +function checkUpdateLogContents( + aCompareLogFile, + aStaged = false, + aReplace = false, + aExcludeDistDir = false +) { + if (AppConstants.platform == "macosx" || AppConstants.platform == "linux") { + // The order that files are returned when enumerating the file system on + // Linux and Mac is not deterministic so skip checking the logs. + return; + } + + let updateLog = getUpdateDirFile(FILE_LAST_UPDATE_LOG); + let updateLogContents = readFileBytes(updateLog); + + // Remove leading timestamps + updateLogContents = removeTimeStamps(updateLogContents); + + // The channel-prefs.js is defined in gTestFilesCommon which will always be + // located to the end of gTestFiles when it is present. + if ( + gTestFiles.length > 1 && + gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && + !gTestFiles[gTestFiles.length - 1].originalContents + ) { + updateLogContents = updateLogContents.replace(/.*defaults\/.*/g, ""); + } + + if ( + gTestFiles.length > 2 && + gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && + !gTestFiles[gTestFiles.length - 2].originalContents + ) { + updateLogContents = updateLogContents.replace( + /.*update-settings.ini.*/g, + "" + ); + } + + // Skip the source/destination lines since they contain absolute paths. + // These could be changed to relative paths using <test_dir_path> and + // <update_dir_path> + updateLogContents = updateLogContents.replace(/PATCH DIRECTORY.*/g, ""); + updateLogContents = updateLogContents.replace( + /INSTALLATION DIRECTORY.*/g, + "" + ); + updateLogContents = updateLogContents.replace(/WORKING DIRECTORY.*/g, ""); + // Skip lines that log failed attempts to open the callback executable. + updateLogContents = updateLogContents.replace( + /NS_main: callback app file .*/g, + "" + ); + // Remove carriage returns. + updateLogContents = updateLogContents.replace(/\r/g, ""); + + if (AppConstants.platform == "win") { + // The FindFile results when enumerating the filesystem on Windows is not + // determistic so the results matching the following need to be fixed. + let re = new RegExp( + // eslint-disable-next-line no-control-regex + "([^\n]* 7/7text1[^\n]*)\n([^\n]* 7/7text0[^\n]*)\n", + "g" + ); + updateLogContents = updateLogContents.replace(re, "$2\n$1\n"); + } + + if (aReplace) { + // Remove the lines which contain absolute paths + updateLogContents = updateLogContents.replace(/^Begin moving.*$/gm, ""); + updateLogContents = updateLogContents.replace( + /^ensure_remove: failed to remove file: .*$/gm, + "" + ); + updateLogContents = updateLogContents.replace( + /^ensure_remove_recursive: unable to remove directory: .*$/gm, + "" + ); + updateLogContents = updateLogContents.replace( + /^Removing tmpDir failed, err: -1$/gm, + "" + ); + updateLogContents = updateLogContents.replace( + /^remove_recursive_on_reboot: .*$/gm, + "" + ); + // Replace requests will retry renaming the installation directory 10 times + // when there are files still in use. The following will remove the + // additional entries from the log file when this happens so the log check + // passes. + let re = new RegExp( + ERR_RENAME_FILE + + "[^\n]*\n" + + "PerformReplaceRequest: destDir rename[^\n]*\n" + + "rename_file: proceeding to rename the directory\n", + "g" + ); + updateLogContents = updateLogContents.replace(re, ""); + } + + // Replace error codes since they are different on each platform. + updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n"); + // Replace to make the log parsing happy. + updateLogContents = updateLogContents.replace(/non-fatal error /g, ""); + // Remove consecutive newlines + updateLogContents = updateLogContents.replace(/\n+/g, "\n"); + // Remove leading and trailing newlines + updateLogContents = updateLogContents.replace(/^\n|\n$/g, ""); + // Replace the log paths with <test_dir_path> and <update_dir_path> + updateLogContents = replaceLogPaths(updateLogContents); + + let compareLogContents = ""; + if (aCompareLogFile) { + compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile)); + } + + if (aStaged) { + compareLogContents = PERFORMING_STAGED_UPDATE + "\n" + compareLogContents; + } + + // Remove leading timestamps + compareLogContents = removeTimeStamps(compareLogContents); + + // The channel-prefs.js is defined in gTestFilesCommon which will always be + // located to the end of gTestFiles. + if ( + gTestFiles.length > 1 && + gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && + !gTestFiles[gTestFiles.length - 1].originalContents + ) { + compareLogContents = compareLogContents.replace(/.*defaults\/.*/g, ""); + } + + if ( + gTestFiles.length > 2 && + gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && + !gTestFiles[gTestFiles.length - 2].originalContents + ) { + compareLogContents = compareLogContents.replace( + /.*update-settings.ini.*/g, + "" + ); + } + + if (aExcludeDistDir) { + compareLogContents = compareLogContents.replace(/.*distribution\/.*/g, ""); + } + + // Remove leading and trailing newlines + compareLogContents = compareLogContents.replace(/\n+/g, "\n"); + // Remove leading and trailing newlines + compareLogContents = compareLogContents.replace(/^\n|\n$/g, ""); + + // Don't write the contents of the file to the log to reduce log spam + // unless there is a failure. + if (compareLogContents == updateLogContents) { + Assert.ok(true, "the update log contents" + MSG_SHOULD_EQUAL); + } else { + logTestInfo("the update log contents are not correct"); + logUpdateLog(FILE_LAST_UPDATE_LOG); + let aryLog = updateLogContents.split("\n"); + let aryCompare = compareLogContents.split("\n"); + // Pushing an empty string to both arrays makes it so either array's length + // can be used in the for loop below without going out of bounds. + aryLog.push(""); + aryCompare.push(""); + // xpcshell tests won't display the entire contents so log the first + // incorrect line. + for (let i = 0; i < aryLog.length; ++i) { + if (aryLog[i] != aryCompare[i]) { + logTestInfo( + "the first incorrect line is line #" + + i + + " and the " + + "value is: '" + + aryLog[i] + + "'" + ); + Assert.equal( + aryLog[i], + aryCompare[i], + "the update log contents" + MSG_SHOULD_EQUAL + ); + } + } + // This should never happen! + do_throw("Unable to find incorrect update log contents!"); + } +} + +/** + * Helper function to check if the update log contains a string. + * + * @param aCheckString + * The string to check if the update log contains. + */ +function checkUpdateLogContains(aCheckString) { + let updateLog = getUpdateDirFile(FILE_LAST_UPDATE_LOG); + let updateLogContents = readFileBytes(updateLog).replace(/\r\n/g, "\n"); + updateLogContents = removeTimeStamps(updateLogContents); + updateLogContents = replaceLogPaths(updateLogContents); + Assert.notEqual( + updateLogContents.indexOf(aCheckString), + -1, + "the update log '" + + updateLog + + "' contents should contain value: '" + + aCheckString + + "'" + ); +} + +/** + * Helper function for updater binary tests for verifying the state of files and + * directories after a successful update. + * + * @param aGetFileFunc + * The function used to get the files in the directory to be checked. + * @param aStageDirExists + * If true the staging directory will be tested for existence and if + * false the staging directory will be tested for non-existence. + * @param aToBeDeletedDirExists + * On Windows, if true the tobedeleted directory will be tested for + * existence and if false the tobedeleted directory will be tested for + * non-existence. On all othere platforms it will be tested for + * non-existence. + */ +function checkFilesAfterUpdateSuccess( + aGetFileFunc, + aStageDirExists = false, + aToBeDeletedDirExists = false +) { + debugDump("testing contents of files after a successful update"); + gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) { + let testFile = aGetFileFunc( + aTestFile.relPathDir + aTestFile.fileName, + true + ); + debugDump("testing file: " + testFile.path); + if (aTestFile.compareFile || aTestFile.compareContents) { + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + + // Skip these tests on Windows since chmod doesn't really set permissions + // on Windows. + if (AppConstants.platform != "win" && aTestFile.comparePerms) { + // Check if the permssions as set in the complete mar file are correct. + Assert.equal( + "0o" + (testFile.permissions & 0xfff).toString(8), + "0o" + (aTestFile.comparePerms & 0xfff).toString(8), + "the file permissions" + MSG_SHOULD_EQUAL + ); + } + + let fileContents1 = readFileBytes(testFile); + let fileContents2 = aTestFile.compareFile + ? readFileBytes(getTestDirFile(aTestFile.compareFile)) + : aTestFile.compareContents; + // Don't write the contents of the file to the log to reduce log spam + // unless there is a failure. + if (fileContents1 == fileContents2) { + Assert.ok(true, "the file contents" + MSG_SHOULD_EQUAL); + } else { + Assert.equal( + fileContents1, + fileContents2, + "the file contents" + MSG_SHOULD_EQUAL + ); + } + } else { + Assert.ok( + !testFile.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path) + ); + } + }); + + debugDump( + "testing operations specified in removed-files were performed " + + "after a successful update" + ); + gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) { + let testDir = aGetFileFunc(aTestDir.relPathDir, true); + debugDump("testing directory: " + testDir.path); + if (aTestDir.dirRemoved) { + Assert.ok( + !testDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(testDir.path) + ); + } else { + Assert.ok(testDir.exists(), MSG_SHOULD_EXIST + getMsgPath(testDir.path)); + + if (aTestDir.files) { + aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { + let testFile = aGetFileFunc(aTestDir.relPathDir + aTestFile, true); + if (aTestDir.filesRemoved) { + Assert.ok( + !testFile.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path) + ); + } else { + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + } + }); + } + + if (aTestDir.subDirs) { + aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { + let testSubDir = aGetFileFunc(aTestDir.relPathDir + aSubDir, true); + Assert.ok( + testSubDir.exists(), + MSG_SHOULD_EXIST + getMsgPath(testSubDir.path) + ); + if (aTestDir.subDirFiles) { + aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { + let testFile = aGetFileFunc( + aTestDir.relPathDir + aSubDir + aTestFile, + true + ); + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + }); + } + }); + } + } + }); + + checkFilesAfterUpdateCommon(aStageDirExists, aToBeDeletedDirExists); +} + +/** + * Helper function for updater binary tests for verifying the state of files and + * directories after a failed update. + * + * @param aGetFileFunc + * The function used to get the files in the directory to be checked. + * @param aStageDirExists + * If true the staging directory will be tested for existence and if + * false the staging directory will be tested for non-existence. + * @param aToBeDeletedDirExists + * On Windows, if true the tobedeleted directory will be tested for + * existence and if false the tobedeleted directory will be tested for + * non-existence. On all othere platforms it will be tested for + * non-existence. + */ +function checkFilesAfterUpdateFailure( + aGetFileFunc, + aStageDirExists = false, + aToBeDeletedDirExists = false +) { + debugDump("testing contents of files after a failed update"); + gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) { + let testFile = aGetFileFunc( + aTestFile.relPathDir + aTestFile.fileName, + true + ); + debugDump("testing file: " + testFile.path); + if (aTestFile.compareFile || aTestFile.compareContents) { + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + + // Skip these tests on Windows since chmod doesn't really set permissions + // on Windows. + if (AppConstants.platform != "win" && aTestFile.comparePerms) { + // Check the original permssions are retained on the file. + Assert.equal( + testFile.permissions & 0xfff, + aTestFile.comparePerms & 0xfff, + "the file permissions" + MSG_SHOULD_EQUAL + ); + } + + let fileContents1 = readFileBytes(testFile); + let fileContents2 = aTestFile.compareFile + ? readFileBytes(getTestDirFile(aTestFile.compareFile)) + : aTestFile.compareContents; + // Don't write the contents of the file to the log to reduce log spam + // unless there is a failure. + if (fileContents1 == fileContents2) { + Assert.ok(true, "the file contents" + MSG_SHOULD_EQUAL); + } else { + Assert.equal( + fileContents1, + fileContents2, + "the file contents" + MSG_SHOULD_EQUAL + ); + } + } else { + Assert.ok( + !testFile.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path) + ); + } + }); + + debugDump( + "testing operations specified in removed-files were not " + + "performed after a failed update" + ); + gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) { + let testDir = aGetFileFunc(aTestDir.relPathDir, true); + Assert.ok(testDir.exists(), MSG_SHOULD_EXIST + getMsgPath(testDir.path)); + + if (aTestDir.files) { + aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { + let testFile = aGetFileFunc(aTestDir.relPathDir + aTestFile, true); + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + }); + } + + if (aTestDir.subDirs) { + aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { + let testSubDir = aGetFileFunc(aTestDir.relPathDir + aSubDir, true); + Assert.ok( + testSubDir.exists(), + MSG_SHOULD_EXIST + getMsgPath(testSubDir.path) + ); + if (aTestDir.subDirFiles) { + aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { + let testFile = aGetFileFunc( + aTestDir.relPathDir + aSubDir + aTestFile, + true + ); + Assert.ok( + testFile.exists(), + MSG_SHOULD_EXIST + getMsgPath(testFile.path) + ); + }); + } + }); + } + }); + + checkFilesAfterUpdateCommon(aStageDirExists, aToBeDeletedDirExists); +} + +/** + * Helper function for updater binary tests for verifying the state of common + * files and directories after a successful or failed update. + * + * @param aStageDirExists + * If true the staging directory will be tested for existence and if + * false the staging directory will be tested for non-existence. + * @param aToBeDeletedDirExists + * On Windows, if true the tobedeleted directory will be tested for + * existence and if false the tobedeleted directory will be tested for + * non-existence. On all othere platforms it will be tested for + * non-existence. + */ +function checkFilesAfterUpdateCommon(aStageDirExists, aToBeDeletedDirExists) { + debugDump("testing extra directories"); + let stageDir = getStageDirFile(); + if (aStageDirExists) { + Assert.ok(stageDir.exists(), MSG_SHOULD_EXIST + getMsgPath(stageDir.path)); + } else { + Assert.ok( + !stageDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(stageDir.path) + ); + } + + let toBeDeletedDirExists = + AppConstants.platform == "win" ? aToBeDeletedDirExists : false; + let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED); + if (toBeDeletedDirExists) { + Assert.ok( + toBeDeletedDir.exists(), + MSG_SHOULD_EXIST + getMsgPath(toBeDeletedDir.path) + ); + } else { + Assert.ok( + !toBeDeletedDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(toBeDeletedDir.path) + ); + } + + let updatingDir = getApplyDirFile("updating"); + Assert.ok( + !updatingDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(updatingDir.path) + ); + + if (stageDir.exists()) { + updatingDir = stageDir.clone(); + updatingDir.append("updating"); + Assert.ok( + !updatingDir.exists(), + MSG_SHOULD_NOT_EXIST + getMsgPath(updatingDir.path) + ); + } + + debugDump( + "testing backup files should not be left behind in the " + + "application directory" + ); + let applyToDir = getApplyDirFile(); + checkFilesInDirRecursive(applyToDir, checkForBackupFiles); + + if (stageDir.exists()) { + debugDump( + "testing backup files should not be left behind in the " + + "staging directory" + ); + checkFilesInDirRecursive(stageDir, checkForBackupFiles); + } +} + +/** + * Helper function for updater binary tests for verifying the contents of the + * updater callback application log which should contain the arguments passed to + * the callback application. + * + * @param appLaunchLog (optional) + * The application log nsIFile to verify. Defaults to the second + * parameter passed to the callback executable (in the apply directory). + */ +function checkCallbackLog( + appLaunchLog = getApplyDirFile(DIR_RESOURCES + gCallbackArgs[1]) +) { + if (!appLaunchLog.exists()) { + // Uses do_timeout instead of do_execute_soon to lessen log spew. + do_timeout(FILE_IN_USE_TIMEOUT_MS, checkCallbackLog); + return; + } + + let expectedLogContents = gCallbackArgs.join("\n") + "\n"; + let logContents = readFile(appLaunchLog); + // It is possible for the log file contents check to occur before the log file + // contents are completely written so wait until the contents are the expected + // value. If the contents are never the expected value then the test will + // fail by timing out after gTimeoutRuns is greater than MAX_TIMEOUT_RUNS or + // the test harness times out the test. + const MAX_TIMEOUT_RUNS = 20000; + if (logContents != expectedLogContents) { + gTimeoutRuns++; + if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { + logTestInfo("callback log contents are not correct"); + // This file doesn't contain full paths so there is no need to call + // replaceLogPaths. + let aryLog = logContents.split("\n"); + let aryCompare = expectedLogContents.split("\n"); + // Pushing an empty string to both arrays makes it so either array's length + // can be used in the for loop below without going out of bounds. + aryLog.push(""); + aryCompare.push(""); + // xpcshell tests won't display the entire contents so log the incorrect + // line. + for (let i = 0; i < aryLog.length; ++i) { + if (aryLog[i] != aryCompare[i]) { + logTestInfo( + "the first incorrect line in the callback log is: " + aryLog[i] + ); + Assert.equal( + aryLog[i], + aryCompare[i], + "the callback log contents" + MSG_SHOULD_EQUAL + ); + } + } + // This should never happen! + do_throw("Unable to find incorrect callback log contents!"); + } + // Uses do_timeout instead of do_execute_soon to lessen log spew. + do_timeout(FILE_IN_USE_TIMEOUT_MS, checkCallbackLog); + return; + } + Assert.ok(true, "the callback log contents" + MSG_SHOULD_EQUAL); + + waitForFilesInUse(); +} + +/** + * Helper function for updater binary tests for getting the log and running + * files created by the test helper binary file when called with the post-update + * command line argument. + * + * @param aSuffix + * The string to append to the post update test helper binary path. + */ +function getPostUpdateFile(aSuffix) { + return getApplyDirFile(DIR_RESOURCES + gPostUpdateBinFile + aSuffix); +} + +/** + * Checks the contents of the updater post update binary log. When completed + * checkPostUpdateAppLogFinished will be called. + */ +async function checkPostUpdateAppLog() { + // Only Mac OS X and Windows support post update. + if (AppConstants.platform == "macosx" || AppConstants.platform == "win") { + let file = getPostUpdateFile(".log"); + await TestUtils.waitForCondition( + () => file.exists(), + "Waiting for file to exist, path: " + file.path + ); + + let expectedContents = "post-update\n"; + await TestUtils.waitForCondition( + () => readFile(file) == expectedContents, + "Waiting for expected file contents: " + expectedContents + ); + } +} + +/** + * Helper function to check if a file is in use on Windows by making a copy of + * a file and attempting to delete the original file. If the deletion is + * successful the copy of the original file is renamed to the original file's + * name and if the deletion is not successful the copy of the original file is + * deleted. + * + * @param aFile + * An nsIFile for the file to be checked if it is in use. + * @return true if the file can't be deleted and false otherwise. + * @throws If called from a platform other than Windows. + */ +function isFileInUse(aFile) { + if (AppConstants.platform != "win") { + do_throw("Windows only function called by a different platform!"); + } + + if (!aFile.exists()) { + debugDump("file does not exist, path: " + aFile.path); + return false; + } + + let fileBak = aFile.parent; + fileBak.append(aFile.leafName + ".bak"); + try { + if (fileBak.exists()) { + fileBak.remove(false); + } + aFile.copyTo(aFile.parent, fileBak.leafName); + aFile.remove(false); + fileBak.moveTo(aFile.parent, aFile.leafName); + debugDump("file is not in use, path: " + aFile.path); + return false; + } catch (e) { + debugDump("file in use, path: " + aFile.path + ", Exception: " + e); + try { + if (fileBak.exists()) { + fileBak.remove(false); + } + } catch (ex) { + logTestInfo( + "unable to remove backup file, path: " + + fileBak.path + + ", Exception: " + + ex + ); + } + } + return true; +} + +/** + * Waits until files that are in use that break tests are no longer in use and + * then calls doTestFinish to end the test. + */ +function waitForFilesInUse() { + if (AppConstants.platform == "win") { + let fileNames = [ + FILE_APP_BIN, + FILE_UPDATER_BIN, + FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, + ]; + for (let i = 0; i < fileNames.length; ++i) { + let file = getApplyDirFile(fileNames[i]); + if (isFileInUse(file)) { + do_timeout(FILE_IN_USE_TIMEOUT_MS, waitForFilesInUse); + return; + } + } + } + + debugDump("calling doTestFinish"); + doTestFinish(); +} + +/** + * Helper function for updater binary tests for verifying there are no update + * backup files left behind after an update. + * + * @param aFile + * An nsIFile to check if it has moz-backup for its extension. + */ +function checkForBackupFiles(aFile) { + Assert.notEqual( + getFileExtension(aFile), + "moz-backup", + "the file's extension should not equal moz-backup" + getMsgPath(aFile.path) + ); +} + +/** + * Helper function for updater binary tests for recursively enumerating a + * directory and calling a callback function with the file as a parameter for + * each file found. + * + * @param aDir + * A nsIFile for the directory to be deleted + * @param aCallback + * A callback function that will be called with the file as a + * parameter for each file found. + */ +function checkFilesInDirRecursive(aDir, aCallback) { + if (!aDir.exists()) { + do_throw("Directory must exist!"); + } + + let dirEntries = aDir.directoryEntries; + while (dirEntries.hasMoreElements()) { + let entry = dirEntries.nextFile; + + if (entry.exists()) { + if (entry.isDirectory()) { + checkFilesInDirRecursive(entry, aCallback); + } else { + aCallback(entry); + } + } + } +} + +/** + * Waits for an update check request to complete and asserts that the results + * are as-expected. + * + * @param aSuccess + * Whether the update check succeeds or not. If aSuccess is true then + * the check should succeed and if aSuccess is false then the check + * should fail. + * @param aExpectedValues + * An object with common values to check. + * @return A promise which will resolve with the nsIUpdateCheckResult object + * once the update check is complete. + */ +async function waitForUpdateCheck(aSuccess, aExpectedValues = {}) { + let check = gUpdateChecker.checkForUpdates(gUpdateChecker.FOREGROUND_CHECK); + let result = await check.result; + Assert.ok(result.checksAllowed, "We should be able to check for updates"); + Assert.equal( + result.succeeded, + aSuccess, + "the update check should " + (aSuccess ? "succeed" : "error") + ); + if (aExpectedValues.updateCount) { + Assert.equal( + aExpectedValues.updateCount, + result.updates.length, + "the update count" + MSG_SHOULD_EQUAL + ); + } + if (aExpectedValues.url) { + Assert.equal( + aExpectedValues.url, + result.request.channel.originalURI.spec, + "the url" + MSG_SHOULD_EQUAL + ); + } + return result; +} + +/** + * Downloads an update and waits for the download onStopRequest. + * + * @param aUpdates + * An array of updates to select from to download an update. + * @param aUpdateCount + * The number of updates in the aUpdates array. + * @param aExpectedStatus + * The download onStopRequest expected status. + * @return A promise which will resolve the first time the update download + * onStopRequest occurs and returns the arguments from onStopRequest. + */ +async function waitForUpdateDownload(aUpdates, aExpectedStatus) { + let bestUpdate = gAUS.selectUpdate(aUpdates); + let success = await gAUS.downloadUpdate(bestUpdate, false); + if (!success) { + do_throw("nsIApplicationUpdateService:downloadUpdate returned " + success); + } + return new Promise(resolve => + gAUS.addDownloadListener({ + onStartRequest: aRequest => {}, + onProgress: (aRequest, aContext, aProgress, aMaxProgress) => {}, + onStatus: (aRequest, aStatus, aStatusText) => {}, + onStopRequest(request, status) { + gAUS.removeDownloadListener(this); + Assert.equal( + aExpectedStatus, + status, + "the download status" + MSG_SHOULD_EQUAL + ); + resolve(request, status); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIRequestObserver", + "nsIProgressEventSink", + ]), + }) + ); +} + +/** + * Helper for starting the http server used by the tests + */ +function start_httpserver() { + let dir = getTestDirFile(); + debugDump("http server directory path: " + dir.path); + + if (!dir.isDirectory()) { + do_throw( + "A file instead of a directory was specified for HttpServer " + + "registerDirectory! Path: " + + dir.path + ); + } + + let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); + gTestserver = new HttpServer(); + gTestserver.registerDirectory("/", dir); + gTestserver.registerPathHandler("/" + gHTTPHandlerPath, pathHandler); + gTestserver.start(-1); + let testserverPort = gTestserver.identity.primaryPort; + // eslint-disable-next-line no-global-assign + gURLData = URL_HOST + ":" + testserverPort + "/"; + debugDump("http server port = " + testserverPort); +} + +/** + * Custom path handler for the http server + * + * @param aMetadata + * The http metadata for the request. + * @param aResponse + * The http response for the request. + */ +function pathHandler(aMetadata, aResponse) { + gUpdateCheckCount += 1; + aResponse.setHeader("Content-Type", "text/xml", false); + aResponse.setStatusLine(aMetadata.httpVersion, 200, "OK"); + aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length); +} + +/** + * Helper for stopping the http server used by the tests + * + * @param aCallback + * The callback to call after stopping the http server. + */ +function stop_httpserver(aCallback) { + Assert.ok(!!aCallback, "the aCallback parameter should be defined"); + gTestserver.stop(aCallback); +} + +/** + * Creates an nsIXULAppInfo + * + * @param aID + * The ID of the test application + * @param aName + * A name for the test application + * @param aVersion + * The version of the application + * @param aPlatformVersion + * The gecko version of the application + */ +function createAppInfo(aID, aName, aVersion, aPlatformVersion) { + updateAppInfo({ + vendor: APP_INFO_VENDOR, + name: aName, + ID: aID, + version: aVersion, + appBuildID: "2007010101", + platformVersion: aPlatformVersion, + platformBuildID: "2007010101", + inSafeMode: false, + logConsoleErrors: true, + OS: "XPCShell", + XPCOMABI: "noarch-spidermonkey", + }); +} + +/** + * Returns the platform specific arguments used by nsIProcess when launching + * the application. + * + * @param aExtraArgs (optional) + * An array of extra arguments to append to the default arguments. + * @return an array of arguments to be passed to nsIProcess. + * + * Note: a shell is necessary to pipe the application's console output which + * would otherwise pollute the xpcshell log. + * + * Command line arguments used when launching the application: + * -no-remote prevents shell integration from being affected by an existing + * application process. + * -test-process-updates makes the application exit after being relaunched by + * the updater. + * the platform specific string defined by PIPE_TO_NULL to output both stdout + * and stderr to null. This is needed to prevent output from the application + * from ending up in the xpchsell log. + */ +function getProcessArgs(aExtraArgs) { + if (!aExtraArgs) { + aExtraArgs = []; + } + + let appBin = getApplyDirFile(DIR_MACOS + FILE_APP_BIN); + Assert.ok(appBin.exists(), MSG_SHOULD_EXIST + ", path: " + appBin.path); + let appBinPath = appBin.path; + + // The profile must be specified for the tests that launch the application to + // run locally when the profiles.ini and installs.ini files already exist. + // We can't use getApplyDirFile to find the profile path because on Windows + // for service tests that would place the profile inside Program Files, and + // this test script has permission to write in Program Files, but the + // application may drop those permissions. So for Windows service tests we + // override that path with the per-test temp directory that xpcshell provides, + // which should be user writable. + let profileDir = appBin.parent.parent; + if (gIsServiceTest && IS_AUTHENTICODE_CHECK_ENABLED) { + profileDir = do_get_tempdir(); + } + profileDir.append("profile"); + let profilePath = profileDir.path; + + let args; + if (AppConstants.platform == "macosx" || AppConstants.platform == "linux") { + let launchScript = getLaunchScript(); + // Precreate the script with executable permissions + launchScript.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY); + + let scriptContents = "#! /bin/sh\n"; + scriptContents += "export XRE_PROFILE_PATH=" + profilePath + "\n"; + scriptContents += + appBinPath + + " -no-remote -test-process-updates " + + aExtraArgs.join(" ") + + " " + + PIPE_TO_NULL; + writeFile(launchScript, scriptContents); + debugDump( + "created " + launchScript.path + " containing:\n" + scriptContents + ); + args = [launchScript.path]; + } else { + args = [ + "/D", + "/Q", + "/C", + appBinPath, + "-profile", + profilePath, + "-no-remote", + "-test-process-updates", + "-wait-for-browser", + ] + .concat(aExtraArgs) + .concat([PIPE_TO_NULL]); + } + return args; +} + +/** + * Gets a file path for the application to dump its arguments into. This is used + * to verify that a callback application is launched. + * + * @return the file for the application to dump its arguments into. + */ +function getAppArgsLogPath() { + let appArgsLog = do_get_file("/" + gTestID + "_app_args_log", true); + if (appArgsLog.exists()) { + appArgsLog.remove(false); + } + let appArgsLogPath = appArgsLog.path; + if (/ /.test(appArgsLogPath)) { + appArgsLogPath = '"' + appArgsLogPath + '"'; + } + return appArgsLogPath; +} + +/** + * Gets the nsIFile reference for the shell script to launch the application. If + * the file exists it will be removed by this function. + * + * @return the nsIFile for the shell script to launch the application. + */ +function getLaunchScript() { + let launchScript = do_get_file("/" + gTestID + "_launch.sh", true); + if (launchScript.exists()) { + launchScript.remove(false); + } + return launchScript; +} + +/** + * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so + * xpcshell tests can run in parallel and to keep the environment clean. + */ +function adjustGeneralPaths() { + let dirProvider = { + getFile: function AGP_DP_getFile(aProp, aPersistent) { + // Set the value of persistent to false so when this directory provider is + // unregistered it will revert back to the original provider. + aPersistent.value = false; + switch (aProp) { + case NS_GRE_DIR: + return getApplyDirFile(DIR_RESOURCES); + case NS_GRE_BIN_DIR: + return getApplyDirFile(DIR_MACOS); + case XRE_EXECUTABLE_FILE: + return getApplyDirFile(DIR_MACOS + FILE_APP_BIN); + case XRE_UPDATE_ROOT_DIR: + return getMockUpdRootD(); + case XRE_OLD_UPDATE_ROOT_DIR: + return getMockUpdRootD(true); + } + return null; + }, + QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]), + }; + let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService); + ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_DIR); + ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_BIN_DIR); + ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); + ds.registerProvider(dirProvider); + registerCleanupFunction(function AGP_cleanup() { + debugDump("start - unregistering directory provider"); + + if (gAppTimer) { + debugDump("start - cancel app timer"); + gAppTimer.cancel(); + gAppTimer = null; + debugDump("finish - cancel app timer"); + } + + if (gProcess && gProcess.isRunning) { + debugDump("start - kill process"); + try { + gProcess.kill(); + } catch (e) { + debugDump("kill process failed, Exception: " + e); + } + gProcess = null; + debugDump("finish - kill process"); + } + + if (gPIDPersistProcess && gPIDPersistProcess.isRunning) { + debugDump("start - kill pid persist process"); + try { + gPIDPersistProcess.kill(); + } catch (e) { + debugDump("kill pid persist process failed, Exception: " + e); + } + gPIDPersistProcess = null; + debugDump("finish - kill pid persist process"); + } + + if (gHandle) { + try { + debugDump("start - closing handle"); + let kernel32 = ctypes.open("kernel32"); + let CloseHandle = kernel32.declare( + "CloseHandle", + ctypes.winapi_abi, + ctypes.bool /* return*/, + ctypes.voidptr_t /* handle*/ + ); + if (!CloseHandle(gHandle)) { + debugDump("call to CloseHandle failed"); + } + kernel32.close(); + gHandle = null; + debugDump("finish - closing handle"); + } catch (e) { + debugDump("call to CloseHandle failed, Exception: " + e); + } + } + + ds.unregisterProvider(dirProvider); + cleanupTestCommon(); + + // Now that our provider is unregistered, reset the lock a second time so + // that we know the lock we're interested in gets released (xpcshell + // doesn't always run a proper XPCOM shutdown sequence, which is where that + // would normally be happening). + let syncManager = Cc[ + "@mozilla.org/updates/update-sync-manager;1" + ].getService(Ci.nsIUpdateSyncManager); + syncManager.resetLock(); + + debugDump("finish - unregistering directory provider"); + }); +} + +/** + * The timer callback to kill the process if it takes too long. + */ +const gAppTimerCallback = { + notify: function TC_notify(aTimer) { + gAppTimer = null; + if (gProcess.isRunning) { + logTestInfo("attempting to kill process"); + gProcess.kill(); + } + Assert.ok(false, "launch application timer expired"); + }, + QueryInterface: ChromeUtils.generateQI(["nsITimerCallback"]), +}; + +/** + * Launches an application to apply an update. + * + * @param aExpectedStatus + * The expected value of update.status when the update finishes. + */ +async function runUpdateUsingApp(aExpectedStatus) { + debugDump("start - launching application to apply update"); + + // The maximum number of milliseconds the process that is launched can run + // before the test will try to kill it. + const APP_TIMER_TIMEOUT = 120000; + let launchBin = getLaunchBin(); + let args = getProcessArgs(); + debugDump("launching " + launchBin.path + " " + args.join(" ")); + + gProcess = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + gProcess.init(launchBin); + + gAppTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + gAppTimer.initWithCallback( + gAppTimerCallback, + APP_TIMER_TIMEOUT, + Ci.nsITimer.TYPE_ONE_SHOT + ); + + setEnvironment(); + + debugDump("launching application"); + gProcess.run(true, args, args.length); + debugDump("launched application exited"); + + resetEnvironment(); + + if (AppConstants.platform == "win") { + waitForApplicationStop(FILE_UPDATER_BIN); + } + + let file = getUpdateDirFile(FILE_UPDATE_STATUS); + await TestUtils.waitForCondition( + () => file.exists(), + "Waiting for file to exist, path: " + file.path + ); + + await TestUtils.waitForCondition( + () => readStatusFile() == aExpectedStatus, + "Waiting for expected status file contents: " + aExpectedStatus + ).catch(e => { + // Instead of throwing let the check below fail the test so the status + // file's contents are logged. + logTestInfo(e); + }); + Assert.equal( + readStatusFile(), + aExpectedStatus, + "the status file state" + MSG_SHOULD_EQUAL + ); + + // Don't check for an update log when the code in nsUpdateDriver.cpp skips + // updating. + if ( + aExpectedStatus != STATE_PENDING && + aExpectedStatus != STATE_PENDING_SVC && + aExpectedStatus != STATE_APPLIED && + aExpectedStatus != STATE_APPLIED_SVC + ) { + // Don't proceed until the update log has been created. + file = getUpdateDirFile(FILE_UPDATE_LOG); + await TestUtils.waitForCondition( + () => file.exists(), + "Waiting for file to exist, path: " + file.path + ); + } + + debugDump("finish - launching application to apply update"); +} + +/* This Mock incremental downloader is used to verify that connection interrupts + * work correctly in updater code. The implementation of the mock incremental + * downloader is very simple, it simply copies the file to the destination + * location. + */ +function initMockIncrementalDownload() { + const INC_CONTRACT_ID = "@mozilla.org/network/incremental-download;1"; + let incrementalDownloadCID = MockRegistrar.register( + INC_CONTRACT_ID, + IncrementalDownload + ); + registerCleanupFunction(() => { + MockRegistrar.unregister(incrementalDownloadCID); + }); +} + +function IncrementalDownload() { + this.wrappedJSObject = this; +} + +IncrementalDownload.prototype = { + /* nsIIncrementalDownload */ + init(uri, file, chunkSize, intervalInSeconds) { + this._destination = file; + this._URI = uri; + this._finalURI = uri; + }, + + start(observer, ctxt) { + // Do the actual operation async to give a chance for observers to add + // themselves. + Services.tm.dispatchToMainThread(() => { + this._observer = observer.QueryInterface(Ci.nsIRequestObserver); + this._ctxt = ctxt; + this._observer.onStartRequest(this); + let mar = getTestDirFile(FILE_SIMPLE_MAR); + mar.copyTo(this._destination.parent, this._destination.leafName); + let status = Cr.NS_OK; + switch (gIncrementalDownloadErrorType++) { + case 0: + status = Cr.NS_ERROR_NET_RESET; + break; + case 1: + status = Cr.NS_ERROR_CONNECTION_REFUSED; + break; + case 2: + status = Cr.NS_ERROR_NET_RESET; + break; + case 3: + status = Cr.NS_OK; + break; + case 4: + status = Cr.NS_ERROR_OFFLINE; + // After we report offline, we want to eventually show offline + // status being changed to online. + Services.tm.dispatchToMainThread(function () { + Services.obs.notifyObservers( + gAUS, + "network:offline-status-changed", + "online" + ); + }); + break; + } + this._observer.onStopRequest(this, status); + }); + }, + + get URI() { + return this._URI; + }, + + get currentSize() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + get destination() { + return this._destination; + }, + + get finalURI() { + return this._finalURI; + }, + + get totalSize() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + /* nsIRequest */ + cancel(aStatus) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + suspend() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + isPending() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + _loadFlags: 0, + get loadFlags() { + return this._loadFlags; + }, + set loadFlags(val) { + this._loadFlags = val; + }, + + _loadGroup: null, + get loadGroup() { + return this._loadGroup; + }, + set loadGroup(val) { + this._loadGroup = val; + }, + + _name: "", + get name() { + return this._name; + }, + + _status: 0, + get status() { + return this._status; + }, + QueryInterface: ChromeUtils.generateQI(["nsIIncrementalDownload"]), +}; + +/** + * Sets the environment that will be used by the application process when it is + * launched. + */ +function setEnvironment() { + if (AppConstants.platform == "win") { + // The tests use nsIProcess to launch the updater and it is simpler to just + // set an environment variable and have the test updater set the current + // working directory than it is to set the current working directory in the + // test itself. + Services.env.set("CURWORKDIRPATH", getApplyDirFile().path); + } + + // Prevent setting the environment more than once. + if (gShouldResetEnv !== undefined) { + return; + } + + gShouldResetEnv = true; + + if ( + AppConstants.platform == "win" && + !Services.env.exists("XRE_NO_WINDOWS_CRASH_DIALOG") + ) { + gAddedEnvXRENoWindowsCrashDialog = true; + debugDump( + "setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " + + "variable to 1... previously it didn't exist" + ); + Services.env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1"); + } + + if (Services.env.exists("XPCOM_MEM_LEAK_LOG")) { + gEnvXPCOMMemLeakLog = Services.env.get("XPCOM_MEM_LEAK_LOG"); + debugDump( + "removing the XPCOM_MEM_LEAK_LOG environment variable... " + + "previous value " + + gEnvXPCOMMemLeakLog + ); + Services.env.set("XPCOM_MEM_LEAK_LOG", ""); + } + + if (Services.env.exists("XPCOM_DEBUG_BREAK")) { + gEnvXPCOMDebugBreak = Services.env.get("XPCOM_DEBUG_BREAK"); + debugDump( + "setting the XPCOM_DEBUG_BREAK environment variable to " + + "warn... previous value " + + gEnvXPCOMDebugBreak + ); + } else { + debugDump( + "setting the XPCOM_DEBUG_BREAK environment variable to " + + "warn... previously it didn't exist" + ); + } + + Services.env.set("XPCOM_DEBUG_BREAK", "warn"); + + if (gEnvForceServiceFallback) { + // This env variable forces the updater to use the service in an invalid + // way, so that it has to fall back to updating without the service. + debugDump("setting MOZ_FORCE_SERVICE_FALLBACK environment variable to 1"); + Services.env.set("MOZ_FORCE_SERVICE_FALLBACK", "1"); + } else if (gIsServiceTest) { + debugDump("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1"); + Services.env.set("MOZ_NO_SERVICE_FALLBACK", "1"); + } +} + +/** + * Sets the environment back to the original values after launching the + * application. + */ +function resetEnvironment() { + // Prevent resetting the environment more than once. + if (gShouldResetEnv !== true) { + return; + } + + gShouldResetEnv = false; + + if (gEnvXPCOMMemLeakLog) { + debugDump( + "setting the XPCOM_MEM_LEAK_LOG environment variable back to " + + gEnvXPCOMMemLeakLog + ); + Services.env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog); + } + + if (gEnvXPCOMDebugBreak) { + debugDump( + "setting the XPCOM_DEBUG_BREAK environment variable back to " + + gEnvXPCOMDebugBreak + ); + Services.env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak); + } else if (Services.env.exists("XPCOM_DEBUG_BREAK")) { + debugDump("clearing the XPCOM_DEBUG_BREAK environment variable"); + Services.env.set("XPCOM_DEBUG_BREAK", ""); + } + + if (AppConstants.platform == "win" && gAddedEnvXRENoWindowsCrashDialog) { + debugDump("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment variable"); + Services.env.set("XRE_NO_WINDOWS_CRASH_DIALOG", ""); + } + + if (gEnvForceServiceFallback) { + debugDump("removing MOZ_FORCE_SERVICE_FALLBACK environment variable"); + Services.env.set("MOZ_FORCE_SERVICE_FALLBACK", ""); + } else if (gIsServiceTest) { + debugDump("removing MOZ_NO_SERVICE_FALLBACK environment variable"); + Services.env.set("MOZ_NO_SERVICE_FALLBACK", ""); + } +} |