Bug 1875502 - Fix tests broken by the previous patch stack that standardizes update initialization r=nalexander,application-update-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D204129
This commit is contained in:
Robin Steuber 2024-05-16 20:01:55 +00:00
Родитель c9a19be7fa
Коммит c288841e23
12 изменённых файлов: 150 добавлений и 63 удалений

Просмотреть файл

@ -286,18 +286,6 @@ const startupPhases = {
// We reach this phase right after showing the first browser window.
// This means that any I/O at this point delayed first paint.
"before first paint": [
{
// bug 1545119
path: "OldUpdRootD:",
condition: WIN,
stat: 1,
},
{
// bug 1446012
path: "UpdRootD:updates/0/update.status",
condition: WIN,
stat: 1,
},
{
path: "XREAppFeat:formautofill@mozilla.org.xpi",
condition: !WIN,

Просмотреть файл

@ -39,7 +39,8 @@ add_task(async function test_override_postupdate_page() {
reloadUpdateManagerData(true);
});
writeUpdatesToXMLFile(XML_UPDATE);
writeFile(XML_UPDATE, getActiveUpdateFile());
writeSuccessUpdateStatusFile();
reloadUpdateManagerData(false);
is(
@ -105,27 +106,41 @@ function reloadUpdateManagerData(skipFiles = false) {
}
/**
* Writes the updates specified to the active-update.xml file.
* Writes the specified text to the specified file.
*
* @param {string} aText
* The updates represented as a string to write to the active-update.xml file.
* The string to write to the file.
* @param {nsIFile} aFile
* The file to write to.
*/
function writeUpdatesToXMLFile(aText) {
function writeFile(aText, aFile) {
const PERMS_FILE = 0o644;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_TRUNCATE = 0x20;
let activeUpdateFile = getActiveUpdateFile();
if (!activeUpdateFile.exists()) {
activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
if (!aFile.exists()) {
aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
Ci.nsIFileOutputStream
);
let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
fos.init(aFile, flags, PERMS_FILE, 0);
fos.write(aText, aText.length);
fos.close();
}
/**
* If we want the update system to treat the update we wrote out as one that it
* just installed, we need to make it think that the update state is
* "succeeded".
*/
function writeSuccessUpdateStatusFile() {
const statusFile = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
statusFile.append("updates");
statusFile.append("0");
statusFile.append("update.status");
writeFile("succeeded", statusFile);
}

Просмотреть файл

@ -120,10 +120,11 @@ add_task(async function test_bug538331() {
if (testCase.openURL) {
actionsXML += ' openURL="' + testCase.openURL + '"';
}
writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
writeFile(XML_PREFIX + actionsXML + XML_SUFFIX, getActiveUpdateFile());
} else {
writeUpdatesToXMLFile(XML_EMPTY);
writeFile(XML_EMPTY, getActiveUpdateFile());
}
writeSuccessUpdateStatusFile();
reloadUpdateManagerData(false);
@ -200,28 +201,41 @@ function reloadUpdateManagerData(skipFiles = false) {
}
/**
* Writes the updates specified to the active-update.xml file.
* Writes the specified text to the specified file.
*
* @param aText
* The updates represented as a string to write to the active-update.xml
* file.
* @param {string} aText
* The string to write to the file.
* @param {nsIFile} aFile
* The file to write to.
*/
function writeUpdatesToXMLFile(aText) {
function writeFile(aText, aFile) {
const PERMS_FILE = 0o644;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_TRUNCATE = 0x20;
let activeUpdateFile = getActiveUpdateFile();
if (!activeUpdateFile.exists()) {
activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
if (!aFile.exists()) {
aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
Ci.nsIFileOutputStream
);
let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
fos.init(aFile, flags, PERMS_FILE, 0);
fos.write(aText, aText.length);
fos.close();
}
/**
* If we want the update system to treat the update we wrote out as one that it
* just installed, we need to make it think that the update state is
* "succeeded".
*/
function writeSuccessUpdateStatusFile() {
const statusFile = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
statusFile.append("updates");
statusFile.append("0");
statusFile.append("update.status");
writeFile("succeeded", statusFile);
}

Просмотреть файл

@ -43,6 +43,7 @@ class PurgeHTTPCacheAtShutdownTestCase(MarionetteTestCase):
return Services.dirsvc.get("UpdRootD", Ci.nsIFile).parent.parent.path;
"""
)
os.makedirs(path, exist_ok=True)
self.lock_dir = Path(path)
def assertNoLocks(self):

Просмотреть файл

@ -44,7 +44,8 @@ add_task(async function test_updatePing() {
activeUpdateFile.remove(false);
reloadUpdateManagerData(true);
});
writeUpdatesToXMLFile(XML_UPDATE);
writeFile(XML_UPDATE, getActiveUpdateFile());
writeSuccessUpdateStatusFile();
reloadUpdateManagerData(false);
// Start monitoring the ping archive.
@ -138,28 +139,41 @@ function reloadUpdateManagerData(skipFiles = false) {
}
/**
* Writes the updates specified to the active-update.xml file.
* Writes the specified text to the specified file.
*
* @param aText
* The updates represented as a string to write to the active-update.xml
* file.
* @param {string} aText
* The string to write to the file.
* @param {nsIFile} aFile
* The file to write to.
*/
function writeUpdatesToXMLFile(aText) {
function writeFile(aText, aFile) {
const PERMS_FILE = 0o644;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_TRUNCATE = 0x20;
let activeUpdateFile = getActiveUpdateFile();
if (!activeUpdateFile.exists()) {
activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
if (!aFile.exists()) {
aFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
Ci.nsIFileOutputStream
);
let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
fos.init(aFile, flags, PERMS_FILE, 0);
fos.write(aText, aText.length);
fos.close();
}
/**
* If we want the update system to treat the update we wrote out as one that it
* just installed, we need to make it think that the update state is
* "succeeded".
*/
function writeSuccessUpdateStatusFile() {
const statusFile = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
statusFile.append("updates");
statusFile.append("0");
statusFile.append("update.status");
writeFile("succeeded", statusFile);
}

Просмотреть файл

@ -1505,7 +1505,7 @@ function cleanupDownloadingUpdate() {
if (status == STATE_DOWNLOADING) {
let statusFile = readyUpdateDir.clone();
statusFile.append(FILE_UPDATE_STATUS);
statusFile.remove();
statusFile.remove(false);
}
}
@ -3409,7 +3409,11 @@ export class UpdateService {
// update checks, because manual update checking uses a completely
// different code path (AppUpdater.sys.mjs creates its own nsIUpdateChecker),
// bypassing this function completely.
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_DISABLED_BY_POLICY);
if (!this.disabledForTesting) {
// This can cause some problems for tests that aren't setup properly for
// this.
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_DISABLED_BY_POLICY);
}
return false;
}

Просмотреть файл

@ -76,6 +76,15 @@ function handleRequest(aRequest, aResponse) {
return;
}
let marBytes = readFileBytes(getTestDataFile(FILE_SIMPLE_MAR));
if (params.firstByteEarly) {
// Sending the first byte early causes the request's `onStartRequest`
// to be fired before the continue file is written.
const firstByte = marBytes[0];
marBytes = marBytes.substring(1);
aResponse.write(firstByte);
}
let retries = 0;
gSlowDownloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
gSlowDownloadTimer.initWithCallback(
@ -92,7 +101,7 @@ function handleRequest(aRequest, aResponse) {
continueFile.remove(false);
}
gSlowDownloadTimer.cancel();
aResponse.write(readFileBytes(getTestDataFile(FILE_SIMPLE_MAR)));
aResponse.write(marBytes);
aResponse.finish();
} catch (e) {}
}
@ -142,6 +151,9 @@ function handleRequest(aRequest, aResponse) {
let url = "";
if (params.useSlowDownloadMar) {
url = URL_HTTP_UPDATE_SJS + "?slowDownloadMar=1";
if (params.useFirstByteEarly) {
url += "&firstByteEarly=1";
}
} else {
url = params.badURL ? BAD_SERVICE_URL : SERVICE_URL;
}

Просмотреть файл

@ -2286,6 +2286,11 @@ function checkSymlink() {
* Sets the active update and related information for updater tests.
*/
async function setupActiveUpdate() {
// The update system being initialized at an unexpected time could cause
// unexpected effects in the reload process. Make sure that initialization
// has already run first.
await gAUS.init();
let pendingState = gIsServiceTest ? STATE_PENDING_SVC : STATE_PENDING;
let patchProps = { state: pendingState };
let patches = getLocalPatchString(patchProps);
@ -3164,6 +3169,11 @@ async function setupUpdaterTest(
{ requiresOmnijar = false } = {}
) {
debugDump("start - updater test setup");
// Make sure that update has already been initialized. If post update
// processing unexpectedly runs between this setup and when we use these
// files, it may clean them up before we get the chance to use them.
await gAUS.init();
let updatesPatchDir = getUpdateDirFile(DIR_PATCH);
if (!updatesPatchDir.exists()) {
updatesPatchDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);

Просмотреть файл

@ -137,24 +137,52 @@ class TestNoWindowUpdateRestart(MarionetteTestCase):
)
self.assertTrue(quit_flags_correct)
# Normally, the update status file would have been removed at this point by Post Update
# Processing. But restarting resets app.update.disabledForTesting, which causes that to be
# skipped, allowing us to look at the update status file directly.
update_status_path = self.marionette.execute_script(
update_status = self.marionette.execute_async_script(
"""
let statusFile = FileUtils.getDir("UpdRootD", ["updates", "0"]);
statusFile.append("update.status");
return statusFile.path;
"""
let [updateURLString, resolve] = arguments;
(async () => {
// Because post update processing happens during early startup and
// `app.update.disabledForTesting` is also set in early startup, it isn't
// especially well defined whether or not post update processing will have run at
// this point. Resolve this by forcing post update processing to run. This is as
// simple as turning off `app.update.disabledForTesting` and calling into
// UpdateManager, since the relevant methods ensure that initialization has run
// as long as update isn't disabled.
// Set the update URL to a local one first to ensure we don't hit the update server
// when we turn off `app.update.disabledForTesting`.
const mockAppInfo = Object.create(Services.appinfo, {
updateURL: {
configurable: true,
enumerable: true,
writable: false,
value: updateURLString,
},
});
Services.appinfo = mockAppInfo;
Services.prefs.setBoolPref("app.update.disabledForTesting", false);
const UM =
Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager);
const history = await UM.getHistory();
if (!history.length) {
return null;
}
return history[0].state;
})().then(resolve);
""",
script_args=(self.marionette.absolute_url("update.xml"),),
)
with open(update_status_path, "r") as f:
# If Firefox was built with "--enable-unverified-updates" (or presumably if we tested
# with an actual, signed update), the update should succeed. Otherwise, it will fail
# with CERT_VERIFY_ERROR (error code 19). Unfortunately, there is no good way to tell
# which of those situations we are in. Luckily, it doesn't matter, because we aren't
# trying to test whether the update applied successfully, just whether the
# "No Window Update Restart" feature works.
self.assertIn(f.read().strip(), ["succeeded", "failed: 19"])
# If Firefox was built with "--enable-unverified-updates" (or presumably if we tested
# with an actual, signed update), the update should succeed. Otherwise, it will fail
# with CERT_VERIFY_ERROR (error code 19). Unfortunately, there is no good way to tell
# which of those situations we are in. Luckily, it doesn't matter, because we aren't
# trying to test whether the update applied successfully, just whether the
# "No Window Update Restart" feature attempted to apply an update.
# So both success and failure are fine. Any in-progress state is not.
self.assertIn(update_status, ["succeeded", "failed"])
def resetUpdate(self):
self.marionette.execute_script(

Просмотреть файл

@ -99,7 +99,7 @@ async function testCleanupSuccessLogsFIFO(
"Last Update Elevated Log"
);
await standardInit();
await testPostUpdateProcessing();
Assert.ok(
!(await gUpdateManager.getDownloadingUpdate()),

Просмотреть файл

@ -103,8 +103,8 @@ async function downloadUpdate(appUpdateAuto, onDownloadStartCallback) {
await gAUS.downloadUpdate(update, true);
}
await continueFileHandler(CONTINUE_DOWNLOAD);
await waitToStartPromise;
await continueFileHandler(CONTINUE_DOWNLOAD);
await downloadFinishedPromise;
// Wait an extra tick after the download has finished. If we try to check for
// another update exactly when "update-downloaded" fires,
@ -173,7 +173,7 @@ async function testUpdateCheckDoesNotStart() {
}
function prepareToDownloadVersion(version, onlyCompleteMar = false) {
let updateUrl = `${APP_UPDATE_SJS_URL}?useSlowDownloadMar=1&appVersion=${version}`;
let updateUrl = `${APP_UPDATE_SJS_URL}?useSlowDownloadMar=1&useFirstByteEarly=1&appVersion=${version}`;
if (onlyCompleteMar) {
updateUrl += "&completePatchOnly=1";
}

Просмотреть файл

@ -32,6 +32,7 @@ async function run_test() {
writeVersionFile("0.9");
// Try to switch the application to the fake staged application.
await runUpdateUsingApp(STATE_AFTER_STAGE);
reloadUpdateManagerData();
await testPostUpdateProcessing();
checkPostUpdateRunningFile(false);
setTestFilesAndDirsForFailure();