Bug 477054 Implement onProgress for the send later service, and fix counts for sending more than once in the same session. r/sr=bienvenu
authorMark Banner <bugzilla@standard8.plus.com>
Sat, 07 Feb 2009 13:49:46 +0000
changeset 1881 f575ed5c6fe9da719adf5de75470345c075dc9eb
parent 1880 adae126ae987b9a4cb1f1d8cc9d927edf506c9d7
child 1882 d62c04aceee0630b94126fe8200ff083148816b0
push idunknown
push userunknown
push dateunknown
bugs477054
Bug 477054 Implement onProgress for the send later service, and fix counts for sending more than once in the same session. r/sr=bienvenu
mailnews/compose/src/nsMsgSendLater.cpp
mailnews/compose/test/unit/data/429891_testcase.eml
mailnews/compose/test/unit/head_compose.js
mailnews/compose/test/unit/test_bug474774.js
mailnews/compose/test/unit/test_sendMessageLater.js
mailnews/compose/test/unit/test_sendMessageLater2.js
--- a/mailnews/compose/src/nsMsgSendLater.cpp
+++ b/mailnews/compose/src/nsMsgSendLater.cpp
@@ -551,16 +551,21 @@ nsMsgSendLater::StartNextMailFileSend()
     mMessagesToSend.Clear();
     mSendingMessages = PR_FALSE;
     NotifyListenersOnStopSending(NS_OK, nsnull, mTotalSendCount, mTotalSentSuccessfully);
     // XXX Should we be releasing references so that we don't hold onto items
     // unnecessarily.
     return NS_OK;
   }
 
+  // Let everyone know about our progress if we've already sent more than one
+  // message.
+  if (mTotalSendCount)
+    NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count());
+
   nsCOMPtr<nsISupports> currentItem;
   rv = mEnumerator->GetNext(getter_AddRefs(currentItem));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mMessage = do_QueryInterface(currentItem); 
   if (!mMessage)
     return NS_ERROR_NOT_AVAILABLE;
 
@@ -667,21 +672,24 @@ nsMsgSendLater::SendUnsentMessages(nsIMs
     if (NS_SUCCEEDED(rv))
     {
       messageHeader = do_QueryInterface(currentItem, &rv);
       if (NS_SUCCEEDED(rv))
         mMessagesToSend.AppendObject(messageHeader);
     }
   }
 
-  // now get an enumerator for our array
+  // Now get an enumerator for our array.
   rv = NS_NewArrayEnumerator(getter_AddRefs(mEnumerator), mMessagesToSend);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // We're now sending messages so its time to signal that and reset our counts.
   mSendingMessages = PR_TRUE;
+  mTotalSentSuccessfully = 0;
+  mTotalSendCount = 0;
 
   // Notify the listeners that we are starting a send.
   NotifyListenersOnStartSending(mMessagesToSend.Count());
 
   return StartNextMailFileSend();
 }
 
 nsresult nsMsgSendLater::SetOrigMsgDisposition()
--- a/mailnews/compose/test/unit/data/429891_testcase.eml
+++ b/mailnews/compose/test/unit/data/429891_testcase.eml
@@ -1,10 +1,10 @@
 From: Invalid User <invalid@invalid.com>
-To: Invalid User <invalid@invalid.com>
+To: Invalid User <to@invalid.com>
 Subject: Big email
 
 012345678901234567890123456789012345678901234567890123456789
 012345678901234567890123456789012345678901234567890123456789
 012345678901234567890123456789012345678901234567890123456789
 012345678901234567890123456789012345678901234567890123456789
 012345678901234567890123456789012345678901234567890123456789
 012345678901234567890123456789012345678901234567890123456789
--- a/mailnews/compose/test/unit/head_compose.js
+++ b/mailnews/compose/test/unit/head_compose.js
@@ -56,8 +56,40 @@ function do_check_transaction(real, expe
   // closed after we have a chance to process it and not them. We therefore
   // excise this from the list
   if (real.them[real.them.length-1] == "QUIT")
     real.them.pop();
 
   do_check_eq(real.them.join(","), expected.join(","));
   dump("Passed test " + test + "\n");
 }
+
+// This listener is designed just to call OnStopCopy() when its OnStopCopy
+// function is called - the rest of the functions are unneeded for a lot of
+// tests.
+var copyListener = {
+  // nsIMsgSendListener
+  onStartSending: function (aMsgID, aMsgSize) {},
+  onProgress: function (aMsgID, aProgress, aProgressMax) {},
+  onStatus: function (aMsgID, aMsg) {},
+  onStopSending: function (aMsgID, aStatus, aMsg, aReturnFile) {},
+  onGetDraftFolderURI: function (aFolderURI) {},
+  onSendNotPerformed: function (aMsgID, aStatus) {},
+
+  // nsIMsgCopyServiceListener
+  OnStartCopy: function () {},
+  OnProgress: function (aProgress, aProgressMax) {},
+  SetMessageKey: function (aKey) {},
+  GetMessageId: function (aMessageId) {},
+  OnStopCopy: function (aStatus) {
+    OnStopCopy(aStatus);
+  },
+
+  // QueryInterface
+  QueryInterface: function (iid) {
+    if (iid.equals(Ci.nsIMsgSendListener) ||
+        iid.equals(Ci.nsIMsgCopyServiceListener) ||
+        iid.equals(Ci.nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
--- a/mailnews/compose/test/unit/test_bug474774.js
+++ b/mailnews/compose/test/unit/test_bug474774.js
@@ -9,17 +9,17 @@ var server;
 var sentFolder;
 var transaction;
 var originalData;
 var finished = false;
 var identity = null;
 var testFile = do_get_file("../mailnews/compose/test/unit/data/429891_testcase.eml");
 
 const kSender = "from@invalid.com";
-const kTo = "invalid@invalid.com";
+const kTo = "to@invalid.com";
 
 var msgSendLater = Cc["@mozilla.org/messengercompose/sendlater;1"]
   .getService(Ci.nsIMsgSendLater);
 
 // This listener handles the post-sending of the actual message and checks the
 // sequence and ensures the data is correct.
 function msll() {
 }
@@ -66,97 +66,57 @@ msll.prototype = {
 
       var thread = gThreadManager.currentThread;
       while (thread.hasPendingEvents())
         thread.processNextEvent(true);
     }
   }
 };
 
-// This listener is used to find out when the copying of the message to the
-// unsent message folder is completed, and hence can fire off the actual
-// sending of the message.
-function copyListener() {
-}
+function OnStopCopy(aStatus)
+{
+  do_test_finished();
+
+  try {
+    do_check_eq(aStatus, 0);
 
-copyListener.prototype = {
-  // nsIMsgSendListener
-  onStartSending: function (aMsgID, aMsgSize) {
-  },
-  onProgress: function (aMsgID, aProgress, aProgressMax) {
-  },
-  onStatus: function (aMsgID, aMsg) {
-  },
-  onStopSending: function (aMsgID, aStatus, aMsg, aReturnFile) {
-  },
-  onGetDraftFolderURI: function (aFolderURI) {
-  },
-  onSendNotPerformed: function (aMsgID, aStatus) {
-  },
+    // Check this is false before we start sending
+    do_check_eq(msgSendLater.sendingMessages, false);
+
+    let folder = msgSendLater.getUnsentMessagesFolder(identity);
 
-  // nsIMsgCopyServiceListener
-  OnStartCopy: function () {
-  },
-  OnProgress: function (aProgress, aProgressMax) {
-  },
-  SetMessageKey: function (aKey) {
-  },
-  GetMessageId: function (aMessageId) {
-  },
-  OnStopCopy: function (aStatus) {
-    do_test_finished();
+    // Check we have a message in the unsent message folder
+    do_check_eq(folder.getTotalMessages(false), 1);
 
-    try {
-      do_check_eq(aStatus, 0);
-
-      // Check this is false before we start sending
-      do_check_eq(msgSendLater.sendingMessages, false);
-
-      let folder = msgSendLater.getUnsentMessagesFolder(identity);
+    // Now do a comparison of what is in the sent mail folder
+    var fileData = loadFileToString(folder.filePath);
 
-      // Check we have a message in the unsent message folder
-      do_check_eq(folder.getTotalMessages(false), 1);
-
-      // Now do a comparison of what is in the sent mail folder
-      var fileData = loadFileToString(folder.filePath);
+    // Skip the headers etc that mailnews adds
+    var pos = fileData.indexOf("From:");
+    do_check_neq(pos, -1);
 
-      // Skip the headers etc that mailnews adds
-      var pos = fileData.indexOf("From:");
-      do_check_neq(pos, -1);
+    fileData = fileData.substr(pos);
 
-      fileData = fileData.substr(pos);
-
-      // Check the data is matching.
-      do_check_eq(originalData, fileData);
+    // Check the data is matching.
+    do_check_eq(originalData, fileData);
 
-      do_test_pending();
-      do_timeout(sendMessageLater(), 0);
-    } catch (e) {
-      do_throw(e);
-    } finally {
-      server.stop();
-
-      var thread = gThreadManager.currentThread;
-      while (thread.hasPendingEvents())
-        thread.processNextEvent(true);
+    do_test_pending();
+    do_timeout(sendMessageLater(), 0);
+  } catch (e) {
+    do_throw(e);
+  } finally {
+    server.stop();
 
-      finished = true;
-    }
-  },
+    var thread = gThreadManager.currentThread;
+    while (thread.hasPendingEvents())
+      thread.processNextEvent(true);
 
-  // QueryInterface
-  QueryInterface: function (iid) {
-    if (iid.equals(Ci.nsIMsgSendListener) ||
-        iid.equals(Ci.nsIMsgCopyServiceListener) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    finished = true;
   }
-};
+}
 
 // This function does the actual send later
 function sendMessageLater()
 {
   do_test_finished();
 
   // Set up the SMTP server.
   server = setupServerDaemon();
@@ -229,20 +189,18 @@ function run_test() {
   // unsent messages folder.
 
   var compFields = Cc["@mozilla.org/messengercompose/composefields;1"]
                      .createInstance(Ci.nsIMsgCompFields);
 
   compFields.from = identity.email;
   compFields.to = kTo;
 
-  var cl = new copyListener(true);
-
   var msgSend = Cc["@mozilla.org/messengercompose/send;1"]
                   .createInstance(Ci.nsIMsgSend);
 
   msgSend.sendMessageFile(identity, "", compFields, testFile,
                           false, false, Ci.nsIMsgSend.nsMsgQueueForLater,
-                          null, cl, null, null);
+                          null, copyListener, null, null);
 
   // Now we wait till we get copy notification of completion.
   do_test_pending();
 }
--- a/mailnews/compose/test/unit/test_sendMessageLater.js
+++ b/mailnews/compose/test/unit/test_sendMessageLater.js
@@ -16,17 +16,17 @@ var server;
 var sentFolder;
 var transaction;
 var originalData;
 var finished = false;
 var identity = null;
 var testFile = do_get_file("../mailnews/compose/test/unit/data/429891_testcase.eml");
 
 const kSender = "from@invalid.com";
-const kTo = "invalid@invalid.com";
+const kTo = "to@invalid.com";
 
 var msgSendLater = Cc["@mozilla.org/messengercompose/sendlater;1"]
   .getService(Ci.nsIMsgSendLater);
 
 // This listener handles the post-sending of the actual message and checks the
 // sequence and ensures the data is correct.
 function msll() {
 }
@@ -73,97 +73,56 @@ msll.prototype = {
 
       var thread = gThreadManager.currentThread;
       while (thread.hasPendingEvents())
         thread.processNextEvent(true);
     }
   }
 };
 
-// This listener is used to find out when the copying of the message to the
-// unsent message folder is completed, and hence can fire off the actual
-// sending of the message.
-function copyListener() {
-}
+function OnStopCopy(aStatus) {
+  do_test_finished();
+
+  try {
+    do_check_eq(aStatus, 0);
 
-copyListener.prototype = {
-  // nsIMsgSendListener
-  onStartSending: function (aMsgID, aMsgSize) {
-  },
-  onProgress: function (aMsgID, aProgress, aProgressMax) {
-  },
-  onStatus: function (aMsgID, aMsg) {
-  },
-  onStopSending: function (aMsgID, aStatus, aMsg, aReturnFile) {
-  },
-  onGetDraftFolderURI: function (aFolderURI) {
-  },
-  onSendNotPerformed: function (aMsgID, aStatus) {
-  },
+    // Check this is false before we start sending
+    do_check_eq(msgSendLater.sendingMessages, false);
+
+    let folder = msgSendLater.getUnsentMessagesFolder(identity);
 
-  // nsIMsgCopyServiceListener
-  OnStartCopy: function () {
-  },
-  OnProgress: function (aProgress, aProgressMax) {
-  },
-  SetMessageKey: function (aKey) {
-  },
-  GetMessageId: function (aMessageId) {
-  },
-  OnStopCopy: function (aStatus) {
-    do_test_finished();
+    // Check we have a message in the unsent message folder
+    do_check_eq(folder.getTotalMessages(false), 1);
 
-    try {
-      do_check_eq(aStatus, 0);
-
-      // Check this is false before we start sending
-      do_check_eq(msgSendLater.sendingMessages, false);
-
-      let folder = msgSendLater.getUnsentMessagesFolder(identity);
+    // Now do a comparison of what is in the sent mail folder
+    var fileData = loadFileToString(folder.filePath);
 
-      // Check we have a message in the unsent message folder
-      do_check_eq(folder.getTotalMessages(false), 1);
-
-      // Now do a comparison of what is in the sent mail folder
-      var fileData = loadFileToString(folder.filePath);
+    // Skip the headers etc that mailnews adds
+    var pos = fileData.indexOf("From:");
+    do_check_neq(pos, -1);
 
-      // Skip the headers etc that mailnews adds
-      var pos = fileData.indexOf("From:");
-      do_check_neq(pos, -1);
+    fileData = fileData.substr(pos);
 
-      fileData = fileData.substr(pos);
-
-      // Check the data is matching.
-      do_check_eq(originalData, fileData);
+    // Check the data is matching.
+    do_check_eq(originalData, fileData);
 
-      do_test_pending();
-      do_timeout(sendMessageLater(), 0);
-    } catch (e) {
-      do_throw(e);
-    } finally {
-      server.stop();
-
-      var thread = gThreadManager.currentThread;
-      while (thread.hasPendingEvents())
-        thread.processNextEvent(true);
+    do_test_pending();
+    do_timeout(sendMessageLater(), 0);
+  } catch (e) {
+    do_throw(e);
+  } finally {
+    server.stop();
 
-      finished = true;
-    }
-  },
+    var thread = gThreadManager.currentThread;
+    while (thread.hasPendingEvents())
+      thread.processNextEvent(true);
 
-  // QueryInterface
-  QueryInterface: function (iid) {
-    if (iid.equals(Ci.nsIMsgSendListener) ||
-        iid.equals(Ci.nsIMsgCopyServiceListener) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    finished = true;
   }
-};
+}
 
 // This function does the actual send later
 function sendMessageLater()
 {
   do_test_finished();
 
   // Set up the SMTP server.
   server = setupServerDaemon();
@@ -236,20 +195,18 @@ function run_test() {
   // unsent messages folder.
 
   var compFields = Cc["@mozilla.org/messengercompose/composefields;1"]
                      .createInstance(Ci.nsIMsgCompFields);
 
   compFields.from = identity.email;
   compFields.to = kTo;
 
-  var cl = new copyListener(true);
-
   var msgSend = Cc["@mozilla.org/messengercompose/send;1"]
                   .createInstance(Ci.nsIMsgSend);
 
   msgSend.sendMessageFile(identity, "", compFields, testFile,
                           false, false, Ci.nsIMsgSend.nsMsgQueueForLater,
-                          null, cl, null, null);
+                          null, copyListener, null, null);
 
   // Now we wait till we get copy notification of completion.
   do_test_pending();
 }
new file mode 100644
--- /dev/null
+++ b/mailnews/compose/test/unit/test_sendMessageLater2.js
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/**
+ * Complex test for the send message later function - including sending multiple
+ * times in the same session.
+ *
+ * XXX: This test is intended to additionally test sending of multiple messages
+ * from one send later instance, however due to the fact we use one connection
+ * per message sent, it is very difficult to consistently get the fake server
+ * reconected in time for the next connection. Thus, sending of multiple
+ * messages is currently disabled (but commented out for local testing if
+ * required), when we fix bug 136871 we should be able to enable the multiple
+ * messages option. 
+ */
+var test = "sendMessageLater";
+var server = null;
+var gSentFolder;
+var transaction;
+var originalData;
+var identity = null;
+var gMsgFile =
+[
+  do_get_file("../mailnews/compose/test/unit/data/message1.eml"),
+  do_get_file("../mailnews/compose/test/unit/data/429891_testcase.eml")
+];
+var gMsgFileData = [];
+var gMsgOrder = [];
+var gCurTestNum = 0;
+var gLastSentMessage = 0;
+
+const kSender = "from@invalid.com";
+const kTo = "to@invalid.com";
+
+var msgSendLater = Cc["@mozilla.org/messengercompose/sendlater;1"]
+                     .getService(Ci.nsIMsgSendLater);
+
+// This listener handles the post-sending of the actual message and checks the
+// sequence and ensures the data is correct.
+function msll() {
+}
+
+msll.prototype = {
+  // nsIMsgSendLaterListener
+  onStartSending: function (aTotal) {
+    do_check_eq(aTotal, gMsgOrder.length);
+    do_check_eq(msgSendLater.sendingMessages, true);
+  },
+  onProgress: function (aCurrentMessage, aTotal) {
+    try {
+      do_check_eq(aTotal, gMsgOrder.length);
+      do_check_eq(gLastSentMessage + 1, aCurrentMessage);
+      gLastSentMessage = aCurrentMessage;
+      do_check_eq(msgSendLater.sendingMessages, true);
+
+      do_check_transaction(transaction,
+                           ["EHLO test",
+                            "MAIL FROM:<" + kSender + "> SIZE=" + gMsgFileData[gMsgOrder[aCurrentMessage - 1]].length,
+                            "RCPT TO:<" + kTo + ">",
+                            "DATA"]);
+      transaction = null;
+
+      // Compare data file to what the server received
+      do_check_eq(gMsgFileData[gMsgOrder[aCurrentMessage - 1]], server._handler.post);
+
+      // XXX We've got more messages to receive so restart the server for the
+      // new connection, at least until bug 136871 is fixed - we reset and stop
+      // the server on exit, the next test runs the server for the next message.
+      do_timeout(0, "doTest(++gCurTestNum)");
+    } catch (e) {
+      do_throw(e);
+    } finally {
+      // Reset
+      server.resetTest();
+
+      // XXX This is the way we currently try and restart the server which
+      // doesn't always work, once we fix bug 136871 just calling resetTest and
+      // ensuring we play the transaction should be enough.
+      server.stop();
+      
+      var thread = gThreadManager.currentThread;
+      while (thread.hasPendingEvents())
+        thread.processNextEvent(true);
+      server.start(SMTP_PORT);
+
+      var thread = gThreadManager.currentThread;
+      while (thread.hasPendingEvents())
+        thread.processNextEvent(true);
+    }
+  },
+  onStatus: function (aMsg) {
+    // XXX Do we really need this?
+  },
+  onStopSending: function (aStatus, aMsg, aTotal, aSuccessful) {
+    try {
+      do_check_eq(aStatus, 0);
+      do_check_eq(aTotal, aSuccessful);
+      do_check_eq(msgSendLater.sendingMessages, false);
+
+      // XXX This is another send multiple messages hack
+      if (!transaction) {
+        server.performTest();
+        transaction = server.playTransaction();
+      }
+
+      do_check_transaction(transaction,
+                           ["EHLO test",
+                            "MAIL FROM:<" + kSender + "> SIZE=" + gMsgFileData[gMsgOrder[aTotal - 1]].length,
+                            "RCPT TO:<" + kTo + ">",
+                            "DATA"]);
+      transaction = null;
+
+      // Compare data file to what the server received
+      do_check_eq(gMsgFileData[gMsgOrder[aTotal - 1]], server._handler.post);
+
+      do_timeout(0, "doTest(++gCurTestNum)");
+    } catch (e) {
+      dump(e);
+      do_throw(e);
+    } finally {
+      server.resetTest();
+      server.stop();
+
+      var thread = gThreadManager.currentThread;
+      while (thread.hasPendingEvents())
+        thread.processNextEvent(true);
+    }
+  }
+};
+
+// This function is used to find out when the copying of the message to the
+// unsent message folder is completed, and hence can fire off the actual
+// sending of the message.
+function OnStopCopy(aStatus)
+{
+  do_check_eq(aStatus, 0);
+
+  // Check this is false before we start sending
+  do_check_eq(msgSendLater.sendingMessages, false);
+
+  // Check we have a message in the unsent message folder
+  do_check_eq(gSentFolder.getTotalMessages(false), gMsgOrder.length);
+
+  // Start the next step after a brief time so that functions can finish
+  // properly
+  do_timeout(0, "doTest(++gCurTestNum);");
+}
+
+function sendMessageLater(aTestFileIndex)
+{
+  gMsgOrder.push(aTestFileIndex);
+
+  // Prepare to actually "send" the message later, i.e. dump it in the
+  // unsent messages folder.
+
+  var compFields = Cc["@mozilla.org/messengercompose/composefields;1"]
+                     .createInstance(Ci.nsIMsgCompFields);
+
+  compFields.from = identity.email;
+  compFields.to = kTo;
+
+  var msgSend = Cc["@mozilla.org/messengercompose/send;1"]
+                  .createInstance(Ci.nsIMsgSend);
+
+  msgSend.sendMessageFile(identity, "", compFields, gMsgFile[aTestFileIndex],
+                          false, false, Ci.nsIMsgSend.nsMsgQueueForLater,
+                          null, copyListener, null, null);
+}
+
+function resetCounts()
+{
+  gMsgOrder = [];
+  gLastSentMessage = 0;
+  do_timeout(0, "doTest(++gCurTestNum);");
+}
+
+// This function does the actual send later
+function sendUnsentMessages()
+{
+  // Handle the server in a try/catch/finally loop so that we always will stop
+  // the server if something fails.
+  try {
+    // Start the fake SMTP server
+    server.start(SMTP_PORT);
+
+    // Send the unsent message
+    msgSendLater.sendUnsentMessages(identity);
+
+    server.performTest();
+
+    transaction = server.playTransaction();
+  } catch (e) {
+    do_throw(e);
+  } finally {
+    server.stop();
+
+    var thread = gThreadManager.currentThread;
+    while (thread.hasPendingEvents())
+      thread.processNextEvent(true);
+  }
+}
+
+function runServerTest()
+{
+  server.performTest();
+
+  transaction = server.playTransaction();
+}
+
+// Beware before commenting out a test
+// -- later tests might just depend on earlier ones
+const gTestArray =
+[
+  // Copying message from file to folder.
+  function testSendLater1() { sendMessageLater(0); },
+
+  // Now send unsent message
+  function testSendUnsentMessages1() { sendUnsentMessages(); },
+
+  function testSentEmpty() {
+    do_check_eq(gSentFolder.getTotalMessages(false), 0);
+    doTest(++gCurTestNum);
+  },
+
+  // This function just resets a few counts where necessary.
+  function testResetCounts() { resetCounts(); },
+
+  // Now copy more messages...
+  function testCopyFileMessage2() {
+    sendMessageLater(1);
+    // XXX Only do one the second time round, as described at the start of the
+    // file.
+    //    sendMessageLater(0);
+  },
+  // ...and send again
+  function testSendUnsentMessages2() { sendUnsentMessages(); },
+
+  // XXX This may be needed if sending more than one message in the second
+  // stage.
+  //  function testRunServer() { runServerTest(); }
+];
+
+function run_test() {
+  // Load in the test files so we have a record of length and their data.
+  for (var i = 0; i < gMsgFile.length; ++i) {
+    gMsgFileData[i] = loadFileToString(gMsgFile[i]);
+  }
+
+  // Ensure we have a local mail account, an normal account and appropriate
+  // servers and identities.
+  loadLocalMailAccount();
+
+  var acctMgr = Cc["@mozilla.org/messenger/account-manager;1"]
+                  .getService(Ci.nsIMsgAccountManager);
+  acctMgr.setSpecialFolders();
+
+  var account = acctMgr.createAccount();
+  incomingServer = acctMgr.createIncomingServer("test", "localhost", "pop3");
+
+  var smtpServer = getBasicSmtpServer();
+  identity = getSmtpIdentity(kSender, smtpServer);
+
+  account.addIdentity(identity);
+  account.defaultIdentity = identity;
+  account.incomingServer = incomingServer;
+
+  gLocalIncomingServer.rootMsgFolder.addSubfolder("Sent");
+
+  gSentFolder = msgSendLater.getUnsentMessagesFolder(identity);
+
+  // Don't copy messages to sent folder for this test
+  identity.doFcc = false;
+
+  // Create and add a listener
+  var messageListener = new msll();
+
+  msgSendLater.addListener(messageListener);
+
+  // Set up the server
+  server = setupServerDaemon();
+  server.setDebugLevel(fsDebugRecv);
+
+  // "Master" do_test_pending(), paired with a do_test_finished() at the end of
+  // all the operations.
+  do_test_pending();
+
+  // Do the test
+  doTest(1);
+}
+
+function doTest(test)
+{
+  dump("doTest " + test + "\n");
+  if (test <= gTestArray.length) {
+    gCurTestNum = test;
+
+    var testFn = gTestArray[test-1];
+
+    // Set a limit in case the notifications haven't arrived (i.e. a problem)
+    do_timeout(10000, "if (gCurTestNum == "+test+")               \
+               do_throw('Notifications not received in 10000 ms for operation "+testFn.name+", current status is '+gCurrStatus);");
+    try {
+      testFn();
+    } catch(ex) {
+      dump(ex);
+      do_throw(ex);
+    }
+  }
+  else {
+    do_test_finished(); // for the one in run_test()
+  }
+}