summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/test/unit/test_folderCompact.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/base/test/unit/test_folderCompact.js')
-rw-r--r--comm/mailnews/base/test/unit/test_folderCompact.js335
1 files changed, 335 insertions, 0 deletions
diff --git a/comm/mailnews/base/test/unit/test_folderCompact.js b/comm/mailnews/base/test/unit/test_folderCompact.js
new file mode 100644
index 0000000000..876672c0e5
--- /dev/null
+++ b/comm/mailnews/base/test/unit/test_folderCompact.js
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Test suite for folder compaction
+ *
+ * Currently tested:
+ * - Compacting local folders
+ * TODO
+ * - Compacting imap offline stores.
+ */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+const { PromiseTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/PromiseTestUtils.jsm"
+);
+
+Services.prefs.setCharPref(
+ "mail.serverDefaultStoreContractID",
+ "@mozilla.org/msgstore/berkeleystore;1"
+);
+
+// Globals
+var gMsgFile1, gMsgFile2, gMsgFile3;
+var gMsg2ID = "200804111417.m3BEHTk4030129@mrapp51.mozilla.org";
+var gMsg3ID = "4849BF7B.2030800@example.com";
+var gLocalFolder2;
+var gLocalFolder3;
+// After a compact (or other operation), this is what we expect the
+// folder size to be.
+var gExpectedFolderSize;
+var gMsgHdrs = [];
+var gExpectedInboxSize;
+var gExpectedFolder2Size;
+var gExpectedFolder3Size;
+// Use this to account for the size of the separating line after the message
+// body. The compactor tries to preserve the convention already in the mbox,
+// and we know that our test messages have CRLFs, so that's what we'll use.
+var gSeparatorLine = "\r\n";
+
+// Transfer message keys between function calls.
+var gMsgKeys = [];
+
+// nsIMsgCopyServiceListener implementation
+var copyListenerWrap = {
+ SetMessageKey(aKey) {
+ let hdr = localAccountUtils.inboxFolder.GetMessageHeader(aKey);
+ gMsgHdrs.push({ hdr, ID: hdr.messageId });
+ },
+ OnStopCopy(aStatus) {
+ // Check: message successfully copied.
+ Assert.equal(aStatus, 0);
+ },
+};
+
+var urlListenerWrap = {
+ OnStopRunningUrl(aUrl, aExitCode) {
+ // Check: message successfully copied.
+ Assert.equal(aExitCode, 0);
+
+ if (gMsgKeys.length > 0) {
+ // Bug 854798: Check if the new message keys are the same as before compaction.
+ let folderMsgs = [...gMsgKeys.folder.messages];
+ // First message was deleted so skip it in the old array.
+ let expectedKeys = [...gMsgKeys].slice(1);
+ Assert.equal(folderMsgs.length, expectedKeys.length);
+ for (let i = 1; i < expectedKeys.length; i++) {
+ Assert.equal(folderMsgs[i], expectedKeys[i]);
+ }
+ gMsgKeys.length = 0;
+ }
+ },
+};
+
+function copyFileMessage(file, destFolder, isDraftOrTemplate) {
+ let listener = new PromiseTestUtils.PromiseCopyListener(copyListenerWrap);
+ MailServices.copy.copyFileMessage(
+ file,
+ destFolder,
+ null,
+ isDraftOrTemplate,
+ 0,
+ "",
+ listener,
+ null
+ );
+ return listener.promise;
+}
+
+function copyMessages(items, isMove, srcFolder, destFolder) {
+ let listener = new PromiseTestUtils.PromiseCopyListener(copyListenerWrap);
+ MailServices.copy.copyMessages(
+ srcFolder,
+ items,
+ destFolder,
+ isMove,
+ listener,
+ null,
+ true
+ );
+ return listener.promise;
+}
+
+function deleteMessages(srcFolder, items) {
+ let listener = new PromiseTestUtils.PromiseCopyListener(copyListenerWrap);
+ srcFolder.deleteMessages(items, null, false, true, listener, true);
+ return listener.promise;
+}
+
+function calculateFolderSize(folder) {
+ let msgDB = folder.msgDatabase;
+ let totalSize = 0;
+ for (let header of msgDB.enumerateMessages()) {
+ totalSize += header.messageSize + gSeparatorLine.length;
+ }
+ return totalSize;
+}
+
+function verifyMsgOffsets(folder) {
+ let msgDB = folder.msgDatabase;
+ let enumerator = msgDB.enumerateMessages();
+ if (enumerator) {
+ for (let header of enumerator) {
+ if (header instanceof Ci.nsIMsgDBHdr) {
+ let storeToken = header.getStringProperty("storeToken");
+ Assert.equal(storeToken, header.messageOffset);
+ }
+ }
+ }
+}
+
+/*
+ * TESTS
+ */
+
+// Beware before commenting out a test -- later tests might just depend on earlier ones
+var gTestArray = [
+ // Copying messages from files
+ async function testCopyFileMessage1() {
+ await copyFileMessage(gMsgFile1, localAccountUtils.inboxFolder, false);
+ },
+ async function testCopyFileMessage2() {
+ await copyFileMessage(gMsgFile2, localAccountUtils.inboxFolder, false);
+ },
+ async function testCopyFileMessage3() {
+ await copyFileMessage(gMsgFile3, localAccountUtils.inboxFolder, true);
+ showMessages(
+ localAccountUtils.inboxFolder,
+ "after initial 3 messages copy to inbox"
+ );
+ },
+
+ // Moving/copying messages
+ async function testCopyMessages1() {
+ await copyMessages(
+ [gMsgHdrs[0].hdr],
+ false,
+ localAccountUtils.inboxFolder,
+ gLocalFolder2
+ );
+ },
+ async function testCopyMessages2() {
+ await copyMessages(
+ [gMsgHdrs[1].hdr, gMsgHdrs[2].hdr],
+ false,
+ localAccountUtils.inboxFolder,
+ gLocalFolder2
+ );
+ showMessages(gLocalFolder2, "after copying 3 messages");
+ },
+ async function testMoveMessages1() {
+ await copyMessages(
+ [gMsgHdrs[0].hdr, gMsgHdrs[1].hdr],
+ true,
+ localAccountUtils.inboxFolder,
+ gLocalFolder3
+ );
+
+ showMessages(localAccountUtils.inboxFolder, "after moving 2 messages");
+ showMessages(gLocalFolder3, "after moving 2 messages");
+ },
+
+ // Deleting messages
+ async function testDeleteMessages1() {
+ // delete to trash
+ // Let's take a moment to re-initialize stuff that got moved
+ var folder3DB = gLocalFolder3.msgDatabase;
+ gMsgHdrs[0].hdr = folder3DB.getMsgHdrForMessageID(gMsgHdrs[0].ID);
+
+ // Store message keys before deletion and compaction.
+ gMsgKeys.folder = gLocalFolder3;
+ for (let header of gLocalFolder3.messages) {
+ gMsgKeys.push(header.messageKey);
+ }
+
+ // Now delete the message
+ await deleteMessages(gLocalFolder3, [gMsgHdrs[0].hdr]);
+
+ showMessages(gLocalFolder3, "after deleting 1 message to trash");
+ },
+ async function compactFolder() {
+ gExpectedFolderSize = calculateFolderSize(gLocalFolder3);
+ Assert.notEqual(gLocalFolder3.expungedBytes, 0);
+ let listener = new PromiseTestUtils.PromiseUrlListener(urlListenerWrap);
+ gLocalFolder3.compact(listener, null);
+ await listener.promise;
+
+ showMessages(gLocalFolder3, "after compact");
+ },
+ async function testDeleteMessages2() {
+ Assert.equal(gExpectedFolderSize, gLocalFolder3.filePath.fileSize);
+ verifyMsgOffsets(gLocalFolder3);
+ var folder2DB = gLocalFolder2.msgDatabase;
+ gMsgHdrs[0].hdr = folder2DB.getMsgHdrForMessageID(gMsgHdrs[0].ID);
+
+ // Store message keys before deletion and compaction.
+ gMsgKeys.folder = gLocalFolder2;
+ for (let header of gLocalFolder2.messages) {
+ gMsgKeys.push(header.messageKey);
+ }
+
+ // Now delete the message
+ await deleteMessages(gLocalFolder2, [gMsgHdrs[0].hdr]);
+
+ showMessages(gLocalFolder2, "after deleting 1 message");
+ },
+ async function compactAllFolders() {
+ gExpectedInboxSize = calculateFolderSize(localAccountUtils.inboxFolder);
+ gExpectedFolder2Size = calculateFolderSize(gLocalFolder2);
+ gExpectedFolder3Size = calculateFolderSize(gLocalFolder3);
+
+ // Save the first message key, which will change after compact with
+ // rebuild.
+ let f2m2Key =
+ gLocalFolder2.msgDatabase.getMsgHdrForMessageID(gMsg2ID).messageKey;
+
+ // force expunged bytes count to get cached.
+ gLocalFolder2.expungedBytes;
+ // mark localFolder2 as having an invalid db, and remove it
+ // for good measure.
+ gLocalFolder2.msgDatabase.summaryValid = false;
+ gLocalFolder2.msgDatabase = null;
+ gLocalFolder2.ForceDBClosed();
+ let dbPath = gLocalFolder2.filePath;
+ dbPath.leafName = dbPath.leafName + ".msf";
+ dbPath.remove(false);
+
+ showMessages(localAccountUtils.inboxFolder, "before compactAll");
+ // Save the key for the inbox message, we'll check after compact that it
+ // did not change.
+ let preInboxMsg3Key =
+ localAccountUtils.inboxFolder.msgDatabase.getMsgHdrForMessageID(
+ gMsg3ID
+ ).messageKey;
+
+ // We used to check here that the keys did not change during rebuild.
+ // But that is no true in general, it was only conicidental since the
+ // checked folder had never been compacted, so the key equaled the offset.
+ // We do not in guarantee that, indeed after rebuild we expect the keys
+ // to change.
+ let checkResult = {
+ OnStopRunningUrl(aUrl, aExitCode) {
+ // Check: message successfully compacted.
+ Assert.equal(aExitCode, 0);
+ },
+ };
+ let listener = new PromiseTestUtils.PromiseUrlListener(checkResult);
+ localAccountUtils.inboxFolder.compactAll(listener, null);
+ await listener.promise;
+
+ showMessages(localAccountUtils.inboxFolder, "after compactAll");
+ showMessages(gLocalFolder2, "after compactAll");
+
+ // For the inbox, which was compacted but not rebuild, key is unchanged.
+ let postInboxMsg3Key =
+ localAccountUtils.inboxFolder.msgDatabase.getMsgHdrForMessageID(
+ gMsg3ID
+ ).messageKey;
+ Assert.equal(preInboxMsg3Key, postInboxMsg3Key);
+
+ // For folder2, which was rebuilt, keys change but all messages should exist.
+ let message2 = gLocalFolder2.msgDatabase.getMsgHdrForMessageID(gMsg2ID);
+ Assert.ok(message2);
+ Assert.ok(gLocalFolder2.msgDatabase.getMsgHdrForMessageID(gMsg3ID));
+
+ // In folder2, gMsg2ID is the first message. After compact with database
+ // rebuild, that key has now changed.
+ Assert.notEqual(message2.messageKey, f2m2Key);
+ },
+ function lastTestCheck() {
+ Assert.equal(
+ gExpectedInboxSize,
+ localAccountUtils.inboxFolder.filePath.fileSize
+ );
+ Assert.equal(gExpectedFolder2Size, gLocalFolder2.filePath.fileSize);
+ Assert.equal(gExpectedFolder3Size, gLocalFolder3.filePath.fileSize);
+ verifyMsgOffsets(gLocalFolder2);
+ verifyMsgOffsets(gLocalFolder3);
+ verifyMsgOffsets(localAccountUtils.inboxFolder);
+ },
+];
+
+function run_test() {
+ localAccountUtils.loadLocalMailAccount();
+ // Load up some messages so that we can copy them in later.
+ gMsgFile1 = do_get_file("../../../data/bugmail10");
+ gMsgFile2 = do_get_file("../../../data/bugmail11");
+ gMsgFile3 = do_get_file("../../../data/draft1");
+
+ // Create another folder to move and copy messages around, and force initialization.
+ gLocalFolder2 = localAccountUtils.rootFolder.createLocalSubfolder("folder2");
+
+ // Create a third folder for more testing.
+ gLocalFolder3 = localAccountUtils.rootFolder.createLocalSubfolder("folder3");
+
+ gTestArray.forEach(x => add_task(x));
+ run_next_test();
+}
+
+// debug utility to show the key/offset/ID relationship of messages in a folder
+function showMessages(folder, text) {
+ dump(`***** Show messages for folder <${folder.name}> "${text} *****\n`);
+ for (let hdr of folder.messages) {
+ dump(
+ ` key: ${hdr.messageKey} offset: ${hdr.messageOffset} size: ${hdr.messageSize} ID: ${hdr.messageId}\n`
+ );
+ }
+}