diff options
| author | Barry Warsaw | 2008-03-06 00:29:11 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-03-06 00:29:11 -0500 |
| commit | 5ca899a81b547dd46197b8d51c7f51538ecde397 (patch) | |
| tree | 43f1ce1d7b94ff46d8e546ab9abf92e4dd9e494e | |
| parent | a1c73f6c305c7f74987d99855ba59d8fa823c253 (diff) | |
| download | mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.gz mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.tar.zst mailman-5ca899a81b547dd46197b8d51c7f51538ecde397.zip | |
| -rw-r--r-- | mailman/database/roster.py | 2 | ||||
| -rw-r--r-- | mailman/pipeline/docs/to-outgoing.txt (renamed from mailman/queue/docs/outgoing.txt) | 0 | ||||
| -rw-r--r-- | mailman/queue/lmtp.py | 2 | ||||
| -rw-r--r-- | mailman/tests/helpers.py | 55 | ||||
| -rw-r--r-- | mailman/tests/smtplistener.py | 87 |
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() |
