From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../imap/test/unit/test_offlineStoreLocking.js | 258 +++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 comm/mailnews/imap/test/unit/test_offlineStoreLocking.js (limited to 'comm/mailnews/imap/test/unit/test_offlineStoreLocking.js') diff --git a/comm/mailnews/imap/test/unit/test_offlineStoreLocking.js b/comm/mailnews/imap/test/unit/test_offlineStoreLocking.js new file mode 100644 index 0000000000..e3a5da62b2 --- /dev/null +++ b/comm/mailnews/imap/test/unit/test_offlineStoreLocking.js @@ -0,0 +1,258 @@ +/* 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 to ensure that code that writes to the imap offline store deals + * with offline store locking correctly. + */ + +/* import-globals-from ../../../test/resources/alertTestUtils.js */ +load("../../../resources/alertTestUtils.js"); + +var { PromiseTestUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/PromiseTestUtils.jsm" +); +var { MessageGenerator } = ChromeUtils.import( + "resource://testing-common/mailnews/MessageGenerator.jsm" +); + +var { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); + +// Globals +var gIMAPTrashFolder, gMsgImapInboxFolder; +var gMovedMsgId; + +var gAlertResolve; +var gGotAlert = new Promise(resolve => { + gAlertResolve = resolve; +}); + +/* exported alert to alertTestUtils.js */ +function alertPS(parent, aDialogTitle, aText) { + gAlertResolve(aText); +} + +function addGeneratedMessagesToServer(messages, mailbox) { + // Create the ImapMessages and store them on the mailbox + messages.forEach(function (message) { + let dataUri = Services.io.newURI( + "data:text/plain;base64," + btoa(message.toMessageString()) + ); + mailbox.addMessage(new ImapMessage(dataUri.spec, mailbox.uidnext++, [])); + }); +} + +var gStreamedHdr = null; + +add_setup(async function () { + registerAlertTestUtils(); + Services.prefs.setBoolPref( + "mail.server.default.autosync_offline_stores", + false + ); + + setupIMAPPump(); + + gMsgImapInboxFolder = IMAPPump.inbox.QueryInterface(Ci.nsIMsgImapMailFolder); + // these hacks are required because we've created the inbox before + // running initial folder discovery, and adding the folder bails + // out before we set it as verified online, so we bail out, and + // then remove the INBOX folder since it's not verified. + gMsgImapInboxFolder.hierarchyDelimiter = "/"; + gMsgImapInboxFolder.verifiedAsOnlineFolder = true; + + let messageGenerator = new MessageGenerator(); + let messages = []; + let bodyString = ""; + for (let i = 0; i < 100; i++) { + bodyString += + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\r\n"; + } + + for (let i = 0; i < 50; i++) { + messages = messages.concat( + messageGenerator.makeMessage({ + body: { body: bodyString, contentType: "text/plain" }, + }) + ); + } + + addGeneratedMessagesToServer(messages, IMAPPump.daemon.getMailbox("INBOX")); + // ...and download for offline use. + let listener = new PromiseTestUtils.PromiseUrlListener(); + IMAPPump.inbox.updateFolderWithListener(null, listener); + await listener.promise; +}); + +add_task(async function downloadForOffline() { + // ...and download for offline use. + let listener = new PromiseTestUtils.PromiseUrlListener(); + IMAPPump.inbox.downloadAllForOffline(listener, null); + await listener.promise; +}); + +add_task(async function deleteOneMsg() { + let enumerator = IMAPPump.inbox.msgDatabase.enumerateMessages(); + let msgHdr = enumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr); + let copyListener = new PromiseTestUtils.PromiseCopyListener(); + IMAPPump.inbox.deleteMessages( + [msgHdr], + null, + false, + true, + copyListener, + false + ); + await copyListener.promise; +}); + +add_task(async function compactOneFolder() { + let enumerator = IMAPPump.inbox.msgDatabase.enumerateMessages(); + let msgHdr = enumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr); + gStreamedHdr = msgHdr; + // Mark the message as not being offline, and then we'll make sure that + // streaming the message while we're compacting doesn't result in the + // message being marked for offline use. + // Luckily, compaction compacts the offline store first, so it should + // lock the offline store. + IMAPPump.inbox.msgDatabase.markOffline(msgHdr.messageKey, false, null); + let msgURI = msgHdr.folder.getUriForMsg(msgHdr); + let msgServ = MailServices.messageServiceFromURI(msgURI); + // UrlListener will get called when both expunge and offline store + // compaction are finished. dummyMsgWindow is required to make the backend + // compact the offline store. + let compactUrlListener = new PromiseTestUtils.PromiseUrlListener(); + IMAPPump.inbox.compact(compactUrlListener, gDummyMsgWindow); + // Stream the message w/o a stream listener in an attempt to get the url + // started more quickly, while the compact is still going on. + let urlListener = new PromiseTestUtils.PromiseUrlListener({}); + await PromiseTestUtils.promiseDelay(100); // But don't be too fast. + msgServ.streamMessage( + msgURI, + new PromiseTestUtils.PromiseStreamListener(), + null, + urlListener, + false, + "", + false + ); + await compactUrlListener.promise; + + // Because we're streaming the message while compaction is going on, + // we should not have stored it for offline use. + Assert.equal(false, gStreamedHdr.flags & Ci.nsMsgMessageFlags.Offline); + + await urlListener.promise; +}); + +add_task(async function deleteAnOtherMsg() { + let enumerator = IMAPPump.inbox.msgDatabase.enumerateMessages(); + let msgHdr = enumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr); + let copyListener = new PromiseTestUtils.PromiseCopyListener(); + IMAPPump.inbox.deleteMessages( + [msgHdr], + null, + false, + true, + copyListener, + false + ); + await copyListener.promise; +}); + +add_task(async function updateTrash() { + gIMAPTrashFolder = IMAPPump.incomingServer.rootFolder + .getChildNamed("Trash") + .QueryInterface(Ci.nsIMsgImapMailFolder); + let listener = new PromiseTestUtils.PromiseUrlListener(); + // hack to force uid validity to get initialized for trash. + gIMAPTrashFolder.updateFolderWithListener(null, listener); + await listener.promise; +}); + +add_task(async function downloadTrashForOffline() { + // ...and download for offline use. + let listener = new PromiseTestUtils.PromiseUrlListener(); + gIMAPTrashFolder.downloadAllForOffline(listener, null); + await listener.promise; +}); + +add_task(async function testOfflineBodyCopy() { + // In order to check that offline copy of messages doesn't try to copy + // the body if the offline store is locked, we're going to go offline. + // Thunderbird itself does move/copies pseudo-offline, but that's too + // hard to test because of the half-second delay. + IMAPPump.server.stop(); + Services.io.offline = true; + let enumerator = gIMAPTrashFolder.msgDatabase.enumerateMessages(); + let msgHdr = enumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr); + gMovedMsgId = msgHdr.messageId; + let compactionListener = new PromiseTestUtils.PromiseUrlListener(); + // NOTE: calling compact() even if msgStore doesn't support compaction. + // It should be a safe no-op, and we're just testing that the listener is + // still invoked. + IMAPPump.inbox.compact(compactionListener, gDummyMsgWindow); + let copyListener = new PromiseTestUtils.PromiseCopyListener(); + MailServices.copy.copyMessages( + gIMAPTrashFolder, + [msgHdr], + IMAPPump.inbox, + true, + copyListener, + null, + true + ); + + // Verify that the moved Msg is not offline. + try { + let movedMsg = + IMAPPump.inbox.msgDatabase.getMsgHdrForMessageID(gMovedMsgId); + Assert.equal(0, movedMsg.flags & Ci.nsMsgMessageFlags.Offline); + } catch (ex) { + throw new Error(ex); + } + await compactionListener.promise; + await copyListener.promise; +}); + +add_task(async function test_checkAlert() { + // Check if testing maildir which doesn't produce an the alert like mbox. + // If so, don't wait for an alert. + let storageCID = Services.prefs.getCharPref( + "mail.serverDefaultStoreContractID" + ); + if (storageCID == "@mozilla.org/msgstore/maildirstore;1") { + return; + } + + let alertText = await gGotAlert; + Assert.ok( + alertText.startsWith( + "The folder 'Inbox on Mail for ' cannot be compacted because another operation is in progress. Please try again later." + ) + ); +}); + +add_task(function teardown() { + gMsgImapInboxFolder = null; + gIMAPTrashFolder = null; + + // IMAPPump.server has already stopped, we do not need to IMAPPump.server.stop(). + IMAPPump.inbox = null; + try { + IMAPPump.incomingServer.closeCachedConnections(); + let serverSink = IMAPPump.incomingServer.QueryInterface( + Ci.nsIImapServerSink + ); + serverSink.abortQueuedUrls(); + } catch (ex) { + throw new Error(ex); + } + let thread = gThreadManager.currentThread; + while (thread.hasPendingEvents()) { + thread.processNextEvent(true); + } +}); -- cgit v1.2.3