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 /comm/mailnews/base/test/unit/test_folderCompact.js | |
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 'comm/mailnews/base/test/unit/test_folderCompact.js')
-rw-r--r-- | comm/mailnews/base/test/unit/test_folderCompact.js | 335 |
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` + ); + } +} |