diff options
Diffstat (limited to 'comm/mailnews/imap/test/unit/test_imapFilterActions.js')
-rw-r--r-- | comm/mailnews/imap/test/unit/test_imapFilterActions.js | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/comm/mailnews/imap/test/unit/test_imapFilterActions.js b/comm/mailnews/imap/test/unit/test_imapFilterActions.js new file mode 100644 index 0000000000..21cd2d01aa --- /dev/null +++ b/comm/mailnews/imap/test/unit/test_imapFilterActions.js @@ -0,0 +1,597 @@ +/* + * This file tests imap filter actions, particularly as affected by the + * addition of body searches in bug 127250. Actions that involves sending + * mail are not tested. The tests check various counts, and the effects + * on the message database of the filters. Effects on IMAP server + * flags, if any, are not tested. + * + * Original author: Kent James <kent@caspia.com> + * adapted from test_localToImapFilter.js + */ + +var Is = Ci.nsMsgSearchOp.Is; +var Contains = Ci.nsMsgSearchOp.Contains; +var Subject = Ci.nsMsgSearchAttrib.Subject; +var Body = Ci.nsMsgSearchAttrib.Body; + +// Globals +var gSubfolder; // a local message folder used as a target for moves and copies +var gFilter; // a message filter with a subject search +var gAction; // current message action (reused) +var gBodyFilter; // a message filter with a body search +var gInboxListener; // database listener object +var gHeader; // the current message db header +var gInboxCount; // the previous number of messages in the Inbox +var gSubfolderCount; // the previous number of messages in the subfolder +var gMessage = "image-attach-test"; // message file used as the test message + +// subject of the test message +var gMessageSubject = "image attach test"; + +// a string in the body of the test message +var gMessageInBody = "01234567890test"; + +// various object references +var gDbService = Cc["@mozilla.org/msgDatabase/msgDBService;1"].getService( + Ci.nsIMsgDBService +); + +var gSmtpServerD = setupSmtpServerDaemon(); + +function setupSmtpServer() { + gSmtpServerD.start(); + var gSmtpServer = localAccountUtils.create_outgoing_server( + gSmtpServerD.port, + "user", + "password", + "localhost" + ); + MailServices.accounts.defaultAccount.defaultIdentity.email = + "from@tinderbox.invalid"; + MailServices.accounts.defaultAccount.defaultIdentity.smtpServerKey = + gSmtpServer.key; + + registerCleanupFunction(() => { + gSmtpServerD.stop(); + Services.prefs.clearUserPref("mail.forward_message_mode"); + }); +} + +// Definition of tests. The test function name is the filter action +// being tested, with "Body" appended to tests that use delayed +// application of filters due to a body search +var gTestArray = [ + setupIMAPPump, + // optionally set server parameters, here enabling debug messages + // function serverParms() { + // IMAPPump.server.setDebugLevel(fsDebugAll); + // }, + setupSmtpServer, + setupFilters, + // The initial tests do not result in new messages added. + async function MoveToFolder() { + gAction.type = Ci.nsMsgFilterAction.MoveToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.equal(gInboxCount, folderCount(IMAPPump.inbox)); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + }, + // do it again, sometimes that causes multiple downloads + async function MoveToFolder2() { + gAction.type = Ci.nsMsgFilterAction.MoveToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.equal(gInboxCount, folderCount(IMAPPump.inbox)); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + }, + /**/ + async function MoveToFolderBody() { + gAction.type = Ci.nsMsgFilterAction.MoveToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gBodyFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + // no net messages were added to the inbox + Assert.equal(gInboxCount, folderCount(IMAPPump.inbox)); + }, + async function MoveToFolderBody2() { + gAction.type = Ci.nsMsgFilterAction.MoveToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gBodyFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + // no net messages were added to the inbox + Assert.equal(gInboxCount, folderCount(IMAPPump.inbox)); + }, + async function MarkRead() { + gAction.type = Ci.nsMsgFilterAction.MarkRead; + gInboxCount = folderCount(IMAPPump.inbox); + await setupTest(gFilter, gAction); + testCounts(false, 0, 0, 0); + Assert.ok(gHeader.isRead); + Assert.equal(gInboxCount + 1, folderCount(IMAPPump.inbox)); + }, + async function MarkReadBody() { + gAction.type = Ci.nsMsgFilterAction.MarkRead; + gInboxCount = folderCount(IMAPPump.inbox); + await setupTest(gBodyFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.ok(gHeader.isRead); + Assert.equal(gInboxCount + 1, folderCount(IMAPPump.inbox)); + }, + async function KillThread() { + gAction.type = Ci.nsMsgFilterAction.KillThread; + await setupTest(gFilter, gAction); + + testCounts(false, 0, 0, 0); + let thread = db().getThreadContainingMsgHdr(gHeader); + Assert.notEqual(0, thread.flags & Ci.nsMsgMessageFlags.Ignored); + }, + async function KillThreadBody() { + gAction.type = Ci.nsMsgFilterAction.KillThread; + await setupTest(gBodyFilter, gAction); + + testCounts(false, 0, 0, 0); + let thread = db().getThreadContainingMsgHdr(gHeader); + Assert.notEqual(0, thread.flags & Ci.nsMsgMessageFlags.Ignored); + }, + async function KillSubthread() { + gAction.type = Ci.nsMsgFilterAction.KillSubthread; + await setupTest(gFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.notEqual(0, gHeader.flags & Ci.nsMsgMessageFlags.Ignored); + }, + async function KillSubthreadBody() { + gAction.type = Ci.nsMsgFilterAction.KillSubthread; + await setupTest(gBodyFilter, gAction); + + testCounts(false, 0, 0, 0); + Assert.notEqual(0, gHeader.flags & Ci.nsMsgMessageFlags.Ignored); + }, + async function DoNothing() { + gAction.type = Ci.nsMsgFilterAction.StopExecution; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + }, + async function DoNothingBody() { + gAction.type = Ci.nsMsgFilterAction.StopExecution; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + }, + // this tests for marking message as junk + async function JunkScore() { + gAction.type = Ci.nsMsgFilterAction.JunkScore; + gAction.junkScore = 100; + await setupTest(gFilter, gAction); + + // marking as junk resets new but not unread + testCounts(false, 1, 0, 0); + Assert.equal(gHeader.getStringProperty("junkscore"), "100"); + Assert.equal(gHeader.getStringProperty("junkscoreorigin"), "filter"); + }, + // this tests for marking message as junk + async function JunkScoreBody() { + gAction.type = Ci.nsMsgFilterAction.JunkScore; + gAction.junkScore = 100; + await setupTest(gBodyFilter, gAction); + + // marking as junk resets new but not unread + testCounts(false, 1, 0, 0); + Assert.equal(gHeader.getStringProperty("junkscore"), "100"); + Assert.equal(gHeader.getStringProperty("junkscoreorigin"), "filter"); + }, + // The remaining tests add new messages + async function MarkUnread() { + gAction.type = Ci.nsMsgFilterAction.MarkUnread; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.ok(!gHeader.isRead); + }, + async function MarkUnreadBody() { + gAction.type = Ci.nsMsgFilterAction.MarkUnread; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.ok(!gHeader.isRead); + }, + async function WatchThread() { + gAction.type = Ci.nsMsgFilterAction.WatchThread; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + let thread = db().getThreadContainingMsgHdr(gHeader); + Assert.notEqual(0, thread.flags & Ci.nsMsgMessageFlags.Watched); + }, + async function WatchThreadBody() { + gAction.type = Ci.nsMsgFilterAction.WatchThread; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + let thread = db().getThreadContainingMsgHdr(gHeader); + Assert.notEqual(0, thread.flags & Ci.nsMsgMessageFlags.Watched); + }, + async function MarkFlagged() { + gAction.type = Ci.nsMsgFilterAction.MarkFlagged; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.ok(gHeader.isFlagged); + }, + async function MarkFlaggedBody() { + gAction.type = Ci.nsMsgFilterAction.MarkFlagged; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.ok(gHeader.isFlagged); + }, + async function ChangePriority() { + gAction.type = Ci.nsMsgFilterAction.ChangePriority; + gAction.priority = Ci.nsMsgPriority.highest; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(Ci.nsMsgPriority.highest, gHeader.priority); + }, + async function ChangePriorityBody() { + gAction.type = Ci.nsMsgFilterAction.ChangePriority; + gAction.priority = Ci.nsMsgPriority.highest; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(Ci.nsMsgPriority.highest, gHeader.priority); + }, + async function AddTag() { + gAction.type = Ci.nsMsgFilterAction.AddTag; + gAction.strValue = "TheTag"; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gHeader.getStringProperty("keywords"), "thetag"); + }, + async function AddTagBody() { + gAction.type = Ci.nsMsgFilterAction.AddTag; + gAction.strValue = "TheTag2"; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gHeader.getStringProperty("keywords"), "thetag2"); + }, + // this tests for marking message as good + async function JunkScoreAsGood() { + gAction.type = Ci.nsMsgFilterAction.JunkScore; + gAction.junkScore = 0; + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gHeader.getStringProperty("junkscore"), "0"); + Assert.equal(gHeader.getStringProperty("junkscoreorigin"), "filter"); + }, + // this tests for marking message as good + async function JunkScoreAsGoodBody() { + gAction.type = Ci.nsMsgFilterAction.JunkScore; + gAction.junkScore = 0; + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gHeader.getStringProperty("junkscore"), "0"); + Assert.equal(gHeader.getStringProperty("junkscoreorigin"), "filter"); + }, + async function CopyToFolder() { + gAction.type = Ci.nsMsgFilterAction.CopyToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gInboxCount + 1, folderCount(IMAPPump.inbox)); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + }, + async function CopyToFolderBody() { + gAction.type = Ci.nsMsgFilterAction.CopyToFolder; + gAction.targetFolderUri = gSubfolder.URI; + gInboxCount = folderCount(IMAPPump.inbox); + gSubfolderCount = folderCount(gSubfolder); + await setupTest(gBodyFilter, gAction); + + testCounts(true, 1, 1, 1); + Assert.equal(gInboxCount + 1, folderCount(IMAPPump.inbox)); + Assert.equal(gSubfolderCount + 1, folderCount(gSubfolder)); + }, + async function ForwardInline() { + return testForward(2); + }, + async function ForwardAsAttachment() { + return testForward(0); + }, + /**/ + endTest, +]; + +function run_test() { + // Services.prefs.setBoolPref("mail.server.default.autosync_offline_stores", false); + gTestArray.forEach(x => add_task(x)); + run_next_test(); +} + +function setupFilters() { + // Create a non-body filter. + let filterList = IMAPPump.incomingServer.getFilterList(null); + gFilter = filterList.createFilter("subject"); + let searchTerm = gFilter.createTerm(); + searchTerm.attrib = Subject; + searchTerm.op = Is; + var value = searchTerm.value; + value.attrib = Subject; + value.str = gMessageSubject; + searchTerm.value = value; + searchTerm.booleanAnd = false; + gFilter.appendTerm(searchTerm); + gFilter.enabled = true; + + // Create a filter with a body term that that forces delayed application of + // filters until after body download. + gBodyFilter = filterList.createFilter("body"); + searchTerm = gBodyFilter.createTerm(); + searchTerm.attrib = Body; + searchTerm.op = Contains; + value = searchTerm.value; + value.attrib = Body; + value.str = gMessageInBody; + searchTerm.value = value; + searchTerm.booleanAnd = false; + gBodyFilter.appendTerm(searchTerm); + gBodyFilter.enabled = true; + + // an action that can be modified by tests + gAction = gFilter.createAction(); + + gSubfolder = localAccountUtils.rootFolder.createLocalSubfolder("Subfolder"); + + MailServices.mailSession.AddFolderListener( + FolderListener, + Ci.nsIFolderListener.event + ); + gPreviousUnread = 0; + + // When a message body is not downloaded, and then later a filter is + // applied that requires a download of message bodies, then the previous + // bodies are downloaded - and the message filters are applied twice! + // See bug 1116228, but for now workaround by always downloading bodies. + IMAPPump.incomingServer.downloadBodiesOnGetNewMail = true; +} + +/* + * functions used to support test setup and execution + */ + +// basic preparation done for each test +async function setupTest(aFilter, aAction) { + let filterList = IMAPPump.incomingServer.getFilterList(null); + while (filterList.filterCount) { + filterList.removeFilterAt(0); + } + if (aFilter) { + aFilter.clearActionList(); + if (aAction) { + aFilter.appendAction(aAction); + filterList.insertFilterAt(0, aFilter); + } + } + if (gInboxListener) { + gDbService.unregisterPendingListener(gInboxListener); + } + + IMAPPump.inbox.clearNewMessages(); + + gInboxListener = new DBListener(); + gDbService.registerPendingListener(IMAPPump.inbox, gInboxListener); + IMAPPump.mailbox.addMessage( + new ImapMessage(specForFileName(gMessage), IMAPPump.mailbox.uidnext++, []) + ); + let promiseUrlListener = new PromiseTestUtils.PromiseUrlListener(); + IMAPPump.inbox.updateFolderWithListener(null, promiseUrlListener); + await promiseUrlListener.promise; + await PromiseTestUtils.promiseDelay(200); +} + +// Cleanup, null out everything, close all cached connections and stop the +// server +function endTest() { + if (gInboxListener) { + gDbService.unregisterPendingListener(gInboxListener); + } + gInboxListener = null; + MailServices.mailSession.RemoveFolderListener(FolderListener); + teardownIMAPPump(); +} + +/* + * listener objects + */ + +// nsIFolderListener implementation +var FolderListener = { + onFolderEvent(aEventFolder, aEvent) { + dump( + "received folder event " + aEvent + " folder " + aEventFolder.name + "\n" + ); + }, +}; + +// nsIDBChangeListener implementation. Counts of calls are kept, but not +// currently used in the tests. Current role is to provide a reference +// to the new message header (plus give some examples of using db listeners +// in javascript). +function DBListener() { + this.counts = {}; + let counts = this.counts; + counts.onHdrFlagsChanged = 0; + counts.onHdrDeleted = 0; + counts.onHdrAdded = 0; + counts.onParentChanged = 0; + counts.onAnnouncerGoingAway = 0; + counts.onReadChanged = 0; + counts.onJunkScoreChanged = 0; + counts.onHdrPropertyChanged = 0; + counts.onEvent = 0; +} + +DBListener.prototype = { + onHdrFlagsChanged(aHdrChanged, aOldFlags, aNewFlags, aInstigator) { + this.counts.onHdrFlagsChanged++; + }, + + onHdrDeleted(aHdrChanged, aParentKey, Flags, aInstigator) { + this.counts.onHdrDeleted++; + }, + + onHdrAdded(aHdrChanged, aParentKey, aFlags, aInstigator) { + this.counts.onHdrAdded++; + gHeader = aHdrChanged; + }, + + onParentChanged(aKeyChanged, oldParent, newParent, aInstigator) { + this.counts.onParentChanged++; + }, + + onAnnouncerGoingAway(instigator) { + if (gInboxListener) { + try { + IMAPPump.inbox.msgDatabase.removeListener(gInboxListener); + } catch (e) { + dump(" listener not found\n"); + } + } + this.counts.onAnnouncerGoingAway++; + }, + + onReadChanged(aInstigator) { + this.counts.onReadChanged++; + }, + + onJunkScoreChanged(aInstigator) { + this.counts.onJunkScoreChanged++; + }, + + onHdrPropertyChanged(aHdrToChange, aPreChange, aStatus, aInstigator) { + this.counts.onHdrPropertyChanged++; + }, + + onEvent(aDB, aEvent) { + this.counts.onEvent++; + }, +}; + +/* + * helper functions + */ + +// return the number of messages in a folder (and check that the +// folder counts match the database counts) +function folderCount(folder) { + // count using the database + let dbCount = [...folder.msgDatabase.enumerateMessages()].length; + + // count using the folder + let count = folder.getTotalMessages(false); + + // compare the two + Assert.equal(dbCount, count); + return dbCount; +} + +// given a test file, return the file uri spec +function specForFileName(aFileName) { + let file = do_get_file("../../../data/" + aFileName); + let msgfileuri = Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL); + return msgfileuri.spec; +} + +// shorthand for the inbox message summary database +function db() { + return IMAPPump.inbox.msgDatabase; +} + +// static variables used in testCounts +var gPreviousUnread = 0; + +// Test various counts. +// +// aHasNew: folder hasNew flag +// aUnreadDelta: change in unread count for the folder +// aFolderNewDelta: change in new count for the folder +// aDbNewDelta: change in new count for the database +// +function testCounts(aHasNew, aUnreadDelta, aFolderNewDelta, aDbNewDelta) { + try { + let folderNew = IMAPPump.inbox.getNumNewMessages(false); + let hasNew = IMAPPump.inbox.hasNewMessages; + let unread = IMAPPump.inbox.getNumUnread(false); + let arrayOut = db().getNewList(); + let dbNew = arrayOut.length; + dump( + " hasNew: " + + hasNew + + " unread: " + + unread + + " folderNew: " + + folderNew + + " dbNew: " + + dbNew + + " prevUnread " + + gPreviousUnread + + "\n" + ); + Assert.equal(aHasNew, hasNew); + Assert.equal(aUnreadDelta, unread - gPreviousUnread); + gPreviousUnread = unread; + // This seems to be reset for each folder update. + // + // This check seems to be failing in SeaMonkey builds, yet I can see no ill + // effects of this in the actual program. Fixing this is complex because of + // the messiness of new count management (see bug 507638 for a + // refactoring proposal, and attachment 398899 on bug 514801 for one possible + // fix to this particular test). So I am disabling this. + // Assert.equal(aFolderNewDelta, folderNew); + Assert.equal(aDbNewDelta, dbNew); + } catch (e) { + dump(e); + } +} + +/** + * Test that Ci.nsMsgFilterAction.Forward works. + * + * @param {number} mode - 0 means forward as attachment, 2 means forward inline. + */ +async function testForward(mode) { + Services.prefs.setIntPref("mail.forward_message_mode", mode); + + gSmtpServerD.resetTest(); + gAction.type = Ci.nsMsgFilterAction.Forward; + gAction.strValue = "to@local"; + await setupTest(gFilter, gAction); + let msgData = gSmtpServerD._daemon.post; + Assert.ok(msgData.includes(`Subject: Fwd: ${gMessageSubject}`)); + Assert.ok(msgData.includes(`${gMessageInBody}`)); +} |