Revision control

Copy as Markdown

Other Tools

/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
*
* Any copyright is dedicated to the Public Domain.
*
* ***** END LICENSE BLOCK ***** */
/*
* Test suite for nsIMsgFolderListener events due to IMAP operations
*
* Currently tested
* - Adding new folders
* - Copying messages from files to mailboxes
* - Adding new messages directly to mailboxes
*
* NOTE (See Bug 1632022):
* Running this test by itself...
*
* $ ./mach xpcshell-test comm/mailnews/imap/test/unit/test_nsIMsgFolderListenerIMAP.js
* ...will fail.
*
* This is because all the IMAP tests run twice - once with mbox storage and
* once with maildir storage. For this test, the two parallel instances
* interact badly.
*
*/
/* import-globals-from ../../../test/resources/msgFolderListenerSetup.js */
load("../../../resources/msgFolderListenerSetup.js");
var { MailServices } = ChromeUtils.importESModule(
);
// Globals
var gRootFolder;
var gIMAPInbox, gIMAPFolder2, gIMAPFolder3;
var gIMAPDaemon, gServer, gIMAPIncomingServer;
var gMsgFile1 = do_get_file("../../../data/bugmail10");
var gMsgFile2 = do_get_file("../../../data/bugmail11");
var gMsgFile3 = do_get_file("../../../data/draft1");
var gMsgFile4 = do_get_file("../../../data/bugmail7");
var gMsgFile5 = do_get_file("../../../data/bugmail6");
// Copied straight from the example files
var gMsgId1 = "200806061706.m56H6RWT004933@mrapp54.mozilla.org";
var gMsgId2 = "200804111417.m3BEHTk4030129@mrapp51.mozilla.org";
var gMsgId3 = "4849BF7B.2030800@example.com";
var gMsgId4 = "bugmail7.m47LtAEf007542@mrapp51.mozilla.org";
var gMsgId5 = "bugmail6.m47LtAEf007542@mrapp51.mozilla.org";
var gMsgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance(
Ci.nsIMsgWindow
);
function addFolder(parent, folderName, storeIn) {
gExpectedEvents = [
[MailServices.mfn.folderAdded, parent, folderName, storeIn],
];
// No copy listener notification for this
gCurrStatus |= kStatus.onStopCopyDone;
parent.createSubfolder(folderName, null);
gCurrStatus |= kStatus.functionCallDone;
gServer.performTest("LIST");
if (gCurrStatus == kStatus.everythingDone) {
resetStatusAndProceed();
}
}
function copyFileMessage(file, messageId, destFolder) {
copyListener.mFolderStoredIn = destFolder;
// This *needs* to be a draft (fourth parameter), as for non-UIDPLUS servers,
// nsImapProtocol::UploadMessageFromFile is hardcoded not to send a copy
// listener notification. The same function also asks for the message id from
// the copy listener, without which it will *not* send the notification.
// ...but wait, nsImapProtocol.cpp requires SEARCH afterwards to retrieve the
// message header, and fakeserver doesn't implement it yet. So get it to fail
// earlier by *not* sending the message id.
// copyListener.mMessageId = messageId;
// Instead store the message id in gExpectedEvents, so we can match that up
gExpectedEvents = [
[MailServices.mfn.msgAdded, { expectedMessageId: messageId }],
[MailServices.mfn.msgsClassified, [messageId], false, false],
];
destFolder.updateFolder(null);
MailServices.copy.copyFileMessage(
file,
destFolder,
null,
true,
0,
"",
copyListener,
null
);
gCurrStatus |= kStatus.functionCallDone;
gServer.performTest("APPEND");
// Allow some time for the append operation to complete, so update folder
// every second
gFolderBeingUpdated = destFolder;
doUpdateFolder(gTest);
}
var gFolderBeingUpdated = null;
function doUpdateFolder(test) {
// In case we've moved on to the next test, exit
if (gTest > test) {
return;
}
gFolderBeingUpdated.updateFolder(null);
if (gCurrStatus == kStatus.everythingDone) {
resetStatusAndProceed();
} else {
do_timeout(1000, function () {
doUpdateFolder(test);
});
}
}
// Adds some messages directly to a mailbox (eg new mail)
function addMessagesToServer(messages, mailbox, localFolder) {
// For every message we have, we need to convert it to a file:/// URI
messages.forEach(function (message) {
const URI = Services.io
.newFileURI(message.file)
.QueryInterface(Ci.nsIFileURL);
// Create the ImapMessage and store it on the mailbox.
mailbox.addMessage(new ImapMessage(URI.spec, mailbox.uidnext++, []));
// We can't get the headers again, so just pass on the message id
gExpectedEvents.push([
MailServices.mfn.msgAdded,
{ expectedMessageId: message.messageId },
]);
});
gExpectedEvents.push([
MailServices.mfn.msgsClassified,
messages.map(hdr => hdr.messageId),
false,
false,
]);
// No copy listener notification for this
gCurrStatus |= kStatus.functionCallDone | kStatus.onStopCopyDone;
gFolderBeingUpdated = localFolder;
doUpdateFolder(gTest);
}
function copyMessages(messages, isMove, srcFolder, destFolder) {
gExpectedEvents = [
[
MailServices.mfn.msgsMoveCopyCompleted,
isMove,
messages,
destFolder,
true,
],
];
// We'll also get the msgAdded events when we go and update the destination
// folder
messages.forEach(function (message) {
// We can't use the headers directly, because the notifications we'll
// receive are for message headers in the destination folder
gExpectedEvents.push([
MailServices.mfn.msgKeyChanged,
{ expectedMessageId: message.messageId },
]);
gExpectedEvents.push([
MailServices.mfn.msgAdded,
{ expectedMessageId: message.messageId },
]);
});
gExpectedEvents.push([
MailServices.mfn.msgsClassified,
messages.map(hdr => hdr.messageId),
false,
false,
]);
MailServices.copy.copyMessages(
srcFolder,
messages,
destFolder,
isMove,
copyListener,
gMsgWindow,
true
);
gCurrStatus |= kStatus.functionCallDone;
gServer.performTest("COPY");
gFolderBeingUpdated = destFolder;
doUpdateFolder(gTest);
if (gCurrStatus == kStatus.everythingDone) {
resetStatusAndProceed();
}
}
var gTestArray = [
// Adding folders
// Create another folder to move and copy messages around, and force initialization.
function testAddFolder1() {
addFolder(gRootFolder, "folder2", function (folder) {
gIMAPFolder2 = folder;
});
},
function testAddFolder2() {
addFolder(gRootFolder, "folder3", function (folder) {
gIMAPFolder3 = folder;
});
},
// Adding messages to folders
function testCopyFileMessage1() {
// Make sure the offline flag is not set for any of the folders
[gIMAPInbox, gIMAPFolder2, gIMAPFolder3].forEach(function (folder) {
folder.clearFlag(Ci.nsMsgFolderFlags.Offline);
});
copyFileMessage(gMsgFile1, gMsgId1, gIMAPInbox);
},
function testCopyFileMessage2() {
copyFileMessage(gMsgFile2, gMsgId2, gIMAPInbox);
},
// Add message straight to the server, so that we get a message added
// notification on the next folder update
function testNewMessageArrival1() {
addMessagesToServer(
[{ file: gMsgFile3, messageId: gMsgId3 }],
gIMAPDaemon.getMailbox("INBOX"),
gIMAPInbox
);
},
// Add another couple of messages, this time to another folder on the server
function testNewMessageArrival2() {
addMessagesToServer(
[
{ file: gMsgFile4, messageId: gMsgId4 },
{ file: gMsgFile5, messageId: gMsgId5 },
],
gIMAPDaemon.getMailbox("INBOX"),
gIMAPInbox
);
},
// Moving/copying messages (this doesn't work right now)
function testCopyMessages1() {
copyMessages(
[gMsgHdrs[0].hdr, gMsgHdrs[1].hdr],
false,
gIMAPInbox,
gIMAPFolder3
);
},
];
function run_test() {
// This is before any of the actual tests, so...
gTest = 0;
gIMAPDaemon = new ImapDaemon();
gServer = makeServer(gIMAPDaemon, "");
gIMAPIncomingServer = createLocalIMAPServer(gServer.port);
// Also make sure a local folders server is created, as that's what is used
// for sent items
localAccountUtils.loadLocalMailAccount();
// We need an identity so that updateFolder doesn't fail
const localAccount = MailServices.accounts.createAccount();
const identity = MailServices.accounts.createIdentity();
localAccount.addIdentity(identity);
localAccount.defaultIdentity = identity;
localAccount.incomingServer = localAccountUtils.incomingServer;
// Let's also have another account, using the same identity
const imapAccount = MailServices.accounts.createAccount();
imapAccount.addIdentity(identity);
imapAccount.defaultIdentity = identity;
imapAccount.incomingServer = gIMAPIncomingServer;
MailServices.accounts.defaultAccount = imapAccount;
// The server doesn't support more than one connection
Services.prefs.setIntPref("mail.server.server1.max_cached_connections", 1);
// Make sure no biff notifications happen
Services.prefs.setBoolPref("mail.biff.play_sound", false);
Services.prefs.setBoolPref("mail.biff.show_alert", false);
Services.prefs.setBoolPref("mail.biff.show_tray_icon", false);
Services.prefs.setBoolPref("mail.biff.animate_dock_icon", false);
// We aren't interested in downloading messages automatically
Services.prefs.setBoolPref("mail.server.server1.download_on_biff", false);
// Add a listener so that we can check all folder events from this point.
MailServices.mfn.addListener(gMFListener, allTestedEvents);
// Get the server list...
gIMAPIncomingServer.performExpand(null);
// We get these notifications on initial discovery
gRootFolder = gIMAPIncomingServer.rootFolder;
gIMAPInbox = gRootFolder.getChildNamed("Inbox");
gExpectedEvents = [
[MailServices.mfn.folderAdded, gRootFolder, "Trash", function () {}],
];
gCurrStatus |= kStatus.onStopCopyDone | kStatus.functionCallDone;
gServer.performTest("SUBSCRIBE");
// "Master" do_test_pending(), paired with a do_test_finished() at the end of
// all the operations.
do_test_pending();
}
function doTest(test) {
// eslint-disable-line no-unused-vars
if (test <= gTestArray.length) {
const testFn = gTestArray[test - 1];
dump(`Doing test ${test} (${testFn.name})\n`);
// Set a limit of ten seconds; if the notifications haven't arrived by then there's a problem.
do_timeout(10000, function () {
if (gTest == test) {
do_throw(
"Notifications not received in 10000 ms for operation " +
testFn.name +
", current status is " +
gCurrStatus
);
}
});
testFn();
} else {
MailServices.mfn.removeListener(gMFListener);
// Cleanup, null out everything, close all cached connections and stop the
// server
gRootFolder = null;
gIMAPInbox.msgDatabase = null;
gIMAPInbox = null;
gIMAPFolder2 = null;
gIMAPFolder3 = null;
do_timeout(1000, endTest);
}
}
function endTest() {
gIMAPIncomingServer.closeCachedConnections();
gServer.performTest();
gServer.stop();
const thread = Services.tm.currentThread;
while (thread.hasPendingEvents()) {
thread.processNextEvent(true);
}
do_test_finished(); // for the one in run_test()
}