summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/test/unit/test_offlineStoreLocking.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/test/unit/test_offlineStoreLocking.js')
-rw-r--r--comm/mailnews/imap/test/unit/test_offlineStoreLocking.js258
1 files changed, 258 insertions, 0 deletions
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);
+ }
+});