summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2008-03-06 00:29:11 -0500
committerBarry Warsaw2008-03-06 00:29:11 -0500
commit5ca899a81b547dd46197b8d51c7f51538ecde397 (patch)
tree43f1ce1d7b94ff46d8e546ab9abf92e4dd9e494e
parenta1c73f6c305c7f74987d99855ba59d8fa823c253 (diff)
downloadmailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.gz
mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.zst
mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.zip
Fix a typo in a roster name.
Remove an outdated comment. Add an SMTPServer wrapper class to the test helpers module. This will be used in tests of the outgoing queue, which actually needs to talk to an SMTP server. Adapt the smtpd-based server to being run under thread control, and remove some now unnecessary code.
Diffstat (limited to '')
-rw-r--r--mailman/database/roster.py2
-rw-r--r--mailman/pipeline/docs/to-outgoing.txt (renamed from mailman/queue/docs/outgoing.txt)0
-rw-r--r--mailman/queue/lmtp.py2
-rw-r--r--mailman/tests/helpers.py55
-rw-r--r--mailman/tests/smtplistener.py87
5 files changed, 71 insertions, 75 deletions
diff --git a/mailman/database/roster.py b/mailman/database/roster.py
index 15191a7c9..2b237df97 100644
--- a/mailman/database/roster.py
+++ b/mailman/database/roster.py
@@ -181,7 +181,7 @@ _digest_modes = (
class DigestMemberRoster(AbstractRoster):
"""Return all the regular delivery members of a list."""
- name = 'regular_members'
+ name = 'digest_members'
@property
def members(self):
diff --git a/mailman/queue/docs/outgoing.txt b/mailman/pipeline/docs/to-outgoing.txt
index 3840b71ee..3840b71ee 100644
--- a/mailman/queue/docs/outgoing.txt
+++ b/mailman/pipeline/docs/to-outgoing.txt
diff --git a/mailman/queue/lmtp.py b/mailman/queue/lmtp.py
index 23aa9b360..71ae2b9cc 100644
--- a/mailman/queue/lmtp.py
+++ b/mailman/queue/lmtp.py
@@ -38,8 +38,6 @@ See the variable USE_LMTP in Defaults.py.in for enabling this delivery
mechanism.
"""
-# NOTE: LMTP delivery is experimental in Mailman 2.2.
-
import os
import email
import smtpd
diff --git a/mailman/tests/helpers.py b/mailman/tests/helpers.py
index 92d652236..4dc289458 100644
--- a/mailman/tests/helpers.py
+++ b/mailman/tests/helpers.py
@@ -32,16 +32,17 @@ import os
import time
import errno
import mailbox
-import subprocess
+import smtplib
+import tempfile
+import threading
+from Queue import Empty, Queue
from datetime import datetime, timedelta
from mailman.bin.master import Loop as Master
from mailman.configuration import config
from mailman.queue import Switchboard
-
-
-WAIT_INTERVAL = timedelta(seconds=3)
+from mailman.tests.smtplistener import Server
@@ -132,3 +133,49 @@ class TestableMaster(Master):
"""The pids of all the child qrunner processes."""
for pid in self._started_kids:
yield pid
+
+
+
+class SMTPServer:
+ """An smtp server for testing."""
+
+ host = 'localhost'
+ port = 9025
+
+ def __init__(self):
+ self._messages = []
+ self._queue = Queue()
+ self._server = Server((self.host, self.port), self._queue)
+ self._thread = threading.Thread(target=self._server.start)
+
+ def start(self):
+ """Start the smtp server in a thread."""
+ self._thread.start()
+
+ def stop(self):
+ """Stop the smtp server."""
+ smtpd = smtplib.SMTP()
+ smtpd.connect(self.host, self.port)
+ smtpd.docmd('EXIT')
+ self.clear()
+ # Wait for the thread to exit.
+ self._thread.join()
+
+ @property
+ def messages(self):
+ """Return all the messages received by the smtp server."""
+ for message in self._messages:
+ # See if there's anything waiting in the queue.
+ try:
+ message = self._queue.get_nowait()
+ except Empty:
+ pass
+ else:
+ self._messages.append(message)
+ yield message
+
+ def clear(self):
+ """Clear all messages from the queue."""
+ # Just throw these away.
+ list(self._messages)
+ self._messages = []
diff --git a/mailman/tests/smtplistener.py b/mailman/tests/smtplistener.py
index 977726247..3a5b870b7 100644
--- a/mailman/tests/smtplistener.py
+++ b/mailman/tests/smtplistener.py
@@ -17,17 +17,12 @@
"""A test SMTP listener."""
-import sys
import smtpd
-import signal
-import mailbox
import asyncore
-import optparse
from email import message_from_string
COMMASPACE = ', '
-DEFAULT_PORT = 9025
@@ -39,10 +34,10 @@ class Channel(smtpd.SMTPChannel):
# Stash this here since the subclass uses private attributes. :(
self._server = server
- def smtp_RSET(self, arg):
- """Respond to RSET and clear the mailbox."""
- self._server.clear_mailbox()
- smtpd.SMTPChannel.smtp_RSET(self, arg)
+ def smtp_EXIT(self, arg):
+ """Respond to a new command EXIT by exiting the server."""
+ self.push('250 Ok')
+ self._server.stop()
def send(self, data):
"""Silence the bloody asynchat/asyncore broken pipe errors!"""
@@ -58,9 +53,9 @@ class Channel(smtpd.SMTPChannel):
class Server(smtpd.SMTPServer):
"""An SMTP server that stores messages to a mailbox."""
- def __init__(self, localaddr, mailbox_path):
+ def __init__(self, localaddr, queue):
smtpd.SMTPServer.__init__(self, localaddr, None)
- self._mailbox = mailbox.Maildir(mailbox_path)
+ self._queue = queue
def handle_accept(self):
"""Handle connections by creating our own Channel object."""
@@ -69,62 +64,18 @@ class Server(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data):
"""Process a message by adding it to the mailbox."""
- msg = message_from_string(data)
- msg['X-Peer'] = peer
- msg['X-MailFrom'] = mailfrom
- msg['X-RcptTo'] = COMMASPACE.join(rcpttos)
- self._mailbox.add(msg)
- self._mailbox.clean()
+ message = message_from_string(data)
+ message['X-Peer'] = peer
+ message['X-MailFrom'] = mailfrom
+ message['X-RcptTo'] = COMMASPACE.join(rcpttos)
+ self._queue.put(message)
+ def start(self):
+ """Start the asyncore loop."""
+ asyncore.loop()
-
-def handle_signal(*ignore):
- """Handle signal sent by parent to kill the process."""
- asyncore.socket_map.clear()
-
-
-
-def main():
- parser = optparse.OptionParser(usage="""\
-%prog [options] mboxfile
-
-This starts a process listening on a specified host and port (by default
-localhost:9025) for SMTP conversations. All messages this process receives
-are stored in a specified mbox file for the parent process to investigate.
-
-This SMTP server responds to RSET commands by clearing the mbox file.
-""")
- parser.add_option('-a', '--address',
- type='string', default=None,
- help='host:port to listen on')
- opts, args = parser.parse_args()
- if len(args) == 0:
- parser.error('Missing mbox file')
- elif len(args) > 1:
- parser.error('Unexpected arguments')
-
- mboxfile = args[0]
- if opts.address is None:
- host = 'localhost'
- port = DEFAULT_PORT
- elif ':' not in opts.address:
- host = opts.address
- port = DEFAULT_PORT
- else:
- host, port = opts.address.split(':', 1)
- port = int(port)
-
- # Catch the parent's exit signal, and also C-c.
- signal.signal(signal.SIGTERM, handle_signal)
- signal.signal(signal.SIGINT, handle_signal)
-
- server = Server((host, port), mboxfile)
- asyncore.loop()
- asyncore.close_all()
- server.close()
- return 0
-
-
-
-if __name__ == '__main__':
- sys.exit(main())
+ def stop(self):
+ """Stop the asyncore loop."""
+ asyncore.socket_map.clear()
+ asyncore.close_all()
+ self.close()