summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Handlers/ToDigest.py60
-rw-r--r--Mailman/bin/testall.py3
-rw-r--r--Mailman/database/model/mailinglist.py2
-rw-r--r--Mailman/docs/digests.txt539
-rw-r--r--Mailman/testing/test_documentation.py12
-rw-r--r--Mailman/testing/test_handlers.py116
6 files changed, 587 insertions, 145 deletions
diff --git a/Mailman/Handlers/ToDigest.py b/Mailman/Handlers/ToDigest.py
index 17ea2f89b..d30f05c71 100644
--- a/Mailman/Handlers/ToDigest.py
+++ b/Mailman/Handlers/ToDigest.py
@@ -52,6 +52,7 @@ from Mailman.Mailbox import Mailbox
from Mailman.MemberAdaptor import ENABLED
from Mailman.Queue.sbcache import get_switchboard
from Mailman.configuration import config
+from Mailman.constants import DeliveryMode, DeliveryStatus
_ = i18n._
__i18n_templates__ = True
@@ -67,7 +68,7 @@ def process(mlist, msg, msgdata):
# Short circuit non-digestable lists.
if not mlist.digestable or msgdata.get('isdigest'):
return
- mboxfile = os.path.join(mlist.fullpath(), 'digest.mbox')
+ mboxfile = os.path.join(mlist.full_path, 'digest.mbox')
mboxfp = open(mboxfile, 'a+')
mbox = Mailbox(mboxfp)
mbox.AppendMessage(msg)
@@ -77,7 +78,7 @@ def process(mlist, msg, msgdata):
# whether the size threshold has been reached.
mboxfp.flush()
size = os.path.getsize(mboxfile)
- if size / 1024.0 >= mlist.digest_size_threshhold:
+ if size / 1024.0 >= mlist.digest_size_threshold:
# This is a bit of a kludge to get the mbox file moved to the digest
# queue directory.
try:
@@ -91,7 +92,7 @@ def process(mlist, msg, msgdata):
except Exception, errmsg:
# Bare except is generally prohibited in Mailman, but we can't
# forecast what exceptions can occur here.
- log.error('send_digests() failed: %s', errmsg)
+ log.exception('send_digests() failed: %s', errmsg)
mboxfp.close()
@@ -155,19 +156,19 @@ def send_i18n_digests(mlist, mboxfp):
mimemsg = Message.Message()
mimemsg['Content-Type'] = 'multipart/mixed'
mimemsg['MIME-Version'] = '1.0'
- mimemsg['From'] = mlist.GetRequestEmail()
+ mimemsg['From'] = mlist.request_address
mimemsg['Subject'] = digestsubj
- mimemsg['To'] = mlist.GetListEmail()
- mimemsg['Reply-To'] = mlist.GetListEmail()
+ mimemsg['To'] = mlist.posting_address
+ mimemsg['Reply-To'] = mlist.posting_address
mimemsg['Date'] = formatdate(localtime=1)
mimemsg['Message-ID'] = Utils.unique_message_id(mlist)
# Set things up for the rfc1153 digest
plainmsg = StringIO()
rfc1153msg = Message.Message()
- rfc1153msg['From'] = mlist.GetRequestEmail()
+ rfc1153msg['From'] = mlist.request_address
rfc1153msg['Subject'] = digestsubj
- rfc1153msg['To'] = mlist.GetListEmail()
- rfc1153msg['Reply-To'] = mlist.GetListEmail()
+ rfc1153msg['To'] = mlist.posting_address
+ rfc1153msg['Reply-To'] = mlist.posting_address
rfc1153msg['Date'] = formatdate(localtime=1)
rfc1153msg['Message-ID'] = Utils.unique_message_id(mlist)
separator70 = '-' * 70
@@ -179,10 +180,10 @@ def send_i18n_digests(mlist, mboxfp):
mastheadtxt = Utils.maketext(
'masthead.txt',
{'real_name' : mlist.real_name,
- 'got_list_email': mlist.GetListEmail(),
+ 'got_list_email': mlist.posting_address,
'got_listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
- 'got_request_email': mlist.GetRequestEmail(),
- 'got_owner_email': mlist.GetOwnerEmail(),
+ 'got_request_email': mlist.request_address,
+ 'got_owner_email': mlist.owner_address,
}, mlist=mlist)
# MIME
masthead = MIMEText(mastheadtxt.encode(lcset), _charset=lcset)
@@ -377,20 +378,31 @@ def send_i18n_digests(mlist, mboxfp):
mlist.next_digest_number += 1
virginq = get_switchboard(config.VIRGINQUEUE_DIR)
# Calculate the recipients lists
- plainrecips = []
- mimerecips = []
- drecips = mlist.getDigestMemberKeys() + mlist.one_last_digest.keys()
- for user in mlist.getMemberCPAddresses(drecips):
- # user might be None if someone who toggled off digest delivery
- # subsequently unsubscribed from the mailing list. Also, filter out
- # folks who have disabled delivery.
- if user is None or mlist.getDeliveryStatus(user) <> ENABLED:
+ plainrecips = set()
+ mimerecips = set()
+ # When someone turns off digest delivery, they will get one last digest to
+ # ensure that there will be no gaps in the messages they receive.
+ # Currently, this dictionary contains the email addresses of those folks
+ # who should get one last digest. We need to find the corresponding
+ # IMember records.
+ digest_members = set(mlist.digest_members.members)
+ for address in mlist.one_last_digest:
+ member = mlist.digest_members.get_member(address)
+ if member:
+ digest_members.add(member)
+ for member in digest_members:
+ if member.delivery_status <> DeliveryStatus.enabled:
continue
- # Otherwise, decide whether they get MIME or RFC 1153 digests
- if mlist.getMemberOption(user, config.DisableMime):
- plainrecips.append(user)
+ # Send the digest to the case-preserved address of the digest members.
+ email_address = member.address.original_address
+ if member.delivery_mode == DeliveryMode.plaintext_digests:
+ plainrecips.add(email_address)
+ elif member.delivery_mode == DeliveryMode.mime_digests:
+ mimerecips.add(email_address)
else:
- mimerecips.append(user)
+ raise AssertionError(
+ 'Digest member "%s" unexpected delivery mode: %s' %
+ (email_address, member.delivery_mode))
# Zap this since we're now delivering the last digest to these folks.
mlist.one_last_digest.clear()
# MIME
diff --git a/Mailman/bin/testall.py b/Mailman/bin/testall.py
index 0b8f6db6b..005808cfe 100644
--- a/Mailman/bin/testall.py
+++ b/Mailman/bin/testall.py
@@ -152,6 +152,9 @@ def main():
if not args:
args = ['.']
+ # Store the options some place that other code can get to it.
+ config.opts = opts
+
# Turn on code coverage if selected.
if opts.coverage:
try:
diff --git a/Mailman/database/model/mailinglist.py b/Mailman/database/model/mailinglist.py
index 71007b83f..fce73cf25 100644
--- a/Mailman/database/model/mailinglist.py
+++ b/Mailman/database/model/mailinglist.py
@@ -91,7 +91,7 @@ class MailingList(Entity):
has_field('digest_header', Unicode),
has_field('digest_is_default', Boolean),
has_field('digest_send_periodic', Boolean),
- has_field('digest_size_threshhold', Integer),
+ has_field('digest_size_threshold', Integer),
has_field('digest_volume_frequency', Integer),
has_field('digestable', Boolean),
has_field('discard_these_nonmembers', PickleType),
diff --git a/Mailman/docs/digests.txt b/Mailman/docs/digests.txt
new file mode 100644
index 000000000..3788651f9
--- /dev/null
+++ b/Mailman/docs/digests.txt
@@ -0,0 +1,539 @@
+Digests
+=======
+
+Digests are a way for a user to receive list traffic in collections instead of
+as individual messages when immediately posted. There are several forms of
+digests, although only two are currently supported: MIME digests and RFC 1153
+(a.k.a. plain text) digests.
+
+ >>> from Mailman.Handlers.ToDigest import process
+ >>> from Mailman.Message import Message
+ >>> from Mailman.Queue.Switchboard import Switchboard
+ >>> from Mailman.configuration import config
+ >>> from Mailman.database import flush
+ >>> from email import message_from_string
+ >>> mlist = config.list_manager.create('_xtest@example.com')
+ >>> mlist.preferred_language = 'en'
+ >>> mlist.web_page_url = 'http://www.example.com/'
+ >>> mlist.real_name = 'XTest'
+ >>> mlist.subject_prefix = '[_XTest] '
+ >>> mlist.one_last_digest = set()
+ >>> flush()
+ >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR)
+
+This is a helper function used to iterate through all the accumulated digest
+messages, in the order in which they were posted. This makes it easier to
+update the tests when we switch to a different mailbox format.
+
+ >>> import os, mailbox
+ >>> def digest_mbox():
+ ... path = os.path.join(mlist.full_path, 'digest.mbox')
+ ... return mailbox.mbox(path)
+
+ >>> def clear_mbox():
+ ... path = os.path.join(mlist.full_path, 'digest.mbox')
+ ... os.remove(path)
+
+ >>> from itertools import count
+ >>> from string import Template
+ >>> def makemsg():
+ ... for i in count(1):
+ ... text = Template("""\
+ ... From: aperson@example.com
+ ... To: _xtest@example.com
+ ... Subject: Test message $i
+ ...
+ ... Here is message $i
+ ... """).substitute(i=i)
+ ... yield message_from_string(text, Message)
+
+
+Short circuiting
+----------------
+
+When a message is posted to the mailing list, it is generally added to a
+running collection of messages. For now, this is a Unix mailbox file,
+although in the future this may end up being converted to a maildir style
+mailbox. In any event, there are several factors that would bypass the
+storing of posted messages to the mailbox. For example, the mailing list may
+not allow digests...
+
+ >>> mlist.digestable = False
+ >>> flush()
+ >>> msg = makemsg().next()
+ >>> process(mlist, msg, {})
+ >>> sum(1 for mboxmsg in digest_mbox())
+ 0
+ >>> switchboard.files
+ []
+
+...or they may allow digests but the message is already a digest.
+
+ >>> mlist.digestable = True
+ >>> flush()
+ >>> process(mlist, msg, dict(isdigest=True))
+ >>> sum(1 for mboxmsg in digest_mbox())
+ 0
+ >>> switchboard.files
+ []
+
+
+Sending a digest
+----------------
+
+For messages which are not digests, but which are posted to a digestable
+mailing list, the messages will be stored until they reach a criteria
+triggering the sending of the digest. If none of those criteria are met, then
+the message will just sit in the mailbox for a while.
+
+ >>> mlist.digest_size_threshold = 10000
+ >>> flush()
+ >>> process(mlist, msg, {})
+ >>> switchboard.files
+ []
+ >>> sum(1 for mboxmsg in digest_mbox())
+ 1
+ >>> clear_mbox()
+
+When the size of the digest mbox reaches the maximum size threshold, a digest
+is crafted and sent out. This puts two messages in the virgin queue, an HTML
+digest and an RFC 1153 plain text digest. The size threshold is in KB.
+
+ >>> mlist.digest_size_threshold = 1
+ >>> mlist.volume = 2
+ >>> mlist.next_digest_number = 10
+ >>> flush()
+ >>> size = 0
+ >>> for msg in makemsg():
+ ... process(mlist, msg, {})
+ ... size += len(str(msg))
+ ... if size > mlist.digest_size_threshold * 1024:
+ ... break
+ >>> sum(1 for mboxmsg in digest_mbox())
+ 0
+ >>> len(switchboard.files)
+ 2
+ >>> for filebase in switchboard.files:
+ ... qmsg, qdata = switchboard.dequeue(filebase)
+ ... switchboard.finish(filebase)
+ ... if qmsg.is_multipart():
+ ... mimemsg = qmsg
+ ... mimedata = qdata
+ ... else:
+ ... rfc1153msg = qmsg
+ ... rfc1153data = qdata
+ >>> print mimemsg.as_string()
+ Content-Type: multipart/mixed; boundary="..."
+ MIME-Version: 1.0
+ From: _xtest-request@example.com
+ Subject: XTest Digest, Vol 2, Issue 10
+ To: _xtest@example.com
+ Reply-To: _xtest@example.com
+ Date: ...
+ Message-ID: ...
+ <BLANKLINE>
+ --...
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Content-Description: XTest Digest, Vol 2, Issue 10
+ <BLANKLINE>
+ Send XTest mailing list submissions to
+ _xtest@example.com
+ <BLANKLINE>
+ To subscribe or unsubscribe via the World Wide Web, visit
+ http://www.example.com/listinfo/_xtest@example.com
+ or, via email, send a message with subject or body 'help' to
+ _xtest-request@example.com
+ <BLANKLINE>
+ You can reach the person managing the list at
+ _xtest-owner@example.com
+ <BLANKLINE>
+ When replying, please edit your Subject line so it is more specific
+ than "Re: Contents of XTest digest..."
+ <BLANKLINE>
+ --...
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Content-Description: Today's Topics (8 messages)
+ <BLANKLINE>
+ Today's Topics:
+ <BLANKLINE>
+ 1. Test message 1 (aperson@example.com)
+ 2. Test message 2 (aperson@example.com)
+ 3. Test message 3 (aperson@example.com)
+ 4. Test message 4 (aperson@example.com)
+ 5. Test message 5 (aperson@example.com)
+ 6. Test message 6 (aperson@example.com)
+ 7. Test message 7 (aperson@example.com)
+ 8. Test message 8 (aperson@example.com)
+ <BLANKLINE>
+ --...
+ Content-Type: multipart/digest; boundary="..."
+ MIME-Version: 1.0
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 1
+ Message: 1
+ <BLANKLINE>
+ Here is message 1
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 2
+ Message: 2
+ <BLANKLINE>
+ Here is message 2
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 3
+ Message: 3
+ <BLANKLINE>
+ Here is message 3
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 4
+ Message: 4
+ <BLANKLINE>
+ Here is message 4
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 5
+ Message: 5
+ <BLANKLINE>
+ Here is message 5
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 6
+ Message: 6
+ <BLANKLINE>
+ Here is message 6
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 7
+ Message: 7
+ <BLANKLINE>
+ Here is message 7
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ From: aperson@example.com
+ To: _xtest@example.com
+ Subject: Test message 8
+ Message: 8
+ <BLANKLINE>
+ Here is message 8
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ --...
+ >>> sorted(mimedata.items())
+ [('_parsemsg', False),
+ ('isdigest', True),
+ ('listname', '_xtest@example.com'),
+ ('received_time', ...),
+ ('recips', set([])), ('version', 3)]
+ >>> print rfc1153msg.as_string()
+ From: _xtest-request@example.com
+ Subject: XTest Digest, Vol 2, Issue 10
+ To: _xtest@example.com
+ Reply-To: _xtest@example.com
+ Date: ...
+ Message-ID: ...
+ MIME-Version: 1.0
+ Content-Type: text/plain; charset="us-ascii"
+ Content-Transfer-Encoding: 7bit
+ <BLANKLINE>
+ Send XTest mailing list submissions to
+ _xtest@example.com
+ <BLANKLINE>
+ To subscribe or unsubscribe via the World Wide Web, visit
+ http://www.example.com/listinfo/_xtest@example.com
+ or, via email, send a message with subject or body 'help' to
+ _xtest-request@example.com
+ <BLANKLINE>
+ You can reach the person managing the list at
+ _xtest-owner@example.com
+ <BLANKLINE>
+ When replying, please edit your Subject line so it is more specific
+ than "Re: Contents of XTest digest..."
+ <BLANKLINE>
+ <BLANKLINE>
+ Today's Topics:
+ <BLANKLINE>
+ 1. Test message 1 (aperson@example.com)
+ 2. Test message 2 (aperson@example.com)
+ 3. Test message 3 (aperson@example.com)
+ 4. Test message 4 (aperson@example.com)
+ 5. Test message 5 (aperson@example.com)
+ 6. Test message 6 (aperson@example.com)
+ 7. Test message 7 (aperson@example.com)
+ 8. Test message 8 (aperson@example.com)
+ <BLANKLINE>
+ <BLANKLINE>
+ ----------------------------------------------------------------------
+ <BLANKLINE>
+ Message: 1
+ From: aperson@example.com
+ Subject: Test message 1
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 1
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 2
+ From: aperson@example.com
+ Subject: Test message 2
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 2
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 3
+ From: aperson@example.com
+ Subject: Test message 3
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 3
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 4
+ From: aperson@example.com
+ Subject: Test message 4
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 4
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 5
+ From: aperson@example.com
+ Subject: Test message 5
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 5
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 6
+ From: aperson@example.com
+ Subject: Test message 6
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 6
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 7
+ From: aperson@example.com
+ Subject: Test message 7
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 7
+ <BLANKLINE>
+ <BLANKLINE>
+ ------------------------------
+ <BLANKLINE>
+ Message: 8
+ From: aperson@example.com
+ Subject: Test message 8
+ To: _xtest@example.com
+ Message-ID: ...
+ <BLANKLINE>
+ Here is message 8
+ <BLANKLINE>
+ <BLANKLINE>
+ End of XTest Digest, Vol 2, Issue 10
+ ************************************
+ <BLANKLINE>
+ >>> sorted(rfc1153data.items())
+ [('_parsemsg', False),
+ ('isdigest', True),
+ ('listname', '_xtest@example.com'),
+ ('received_time', ...),
+ ('recips', set([])), ('version', 3)]
+
+
+Internationalized digests
+-------------------------
+
+When messages come in with a content-type character set different than that of
+the list's preferred language, recipients wil get an internationalized digest.
+
+ >>> mlist.preferred_language = 'fr'
+ >>> msg = message_from_string("""\
+ ... From: aperson@example.org
+ ... To: _xtest@example.com
+ ... Subject: =?iso-2022-jp?b?GyRCMGxIVhsoQg==?=
+ ... MIME-Version: 1.0
+ ... Content-Type: text/plain; charset=iso-2022-jp
+ ... Content-Transfer-Encoding: 7bit
+ ...
+ ... \x1b$B0lHV\x1b(B
+ ... """, Message)
+
+Set the digest threshold to zero so that the digests will be sent immediately.
+
+ >>> mlist.digest_size_threshold = 0
+ >>> flush()
+ >>> process(mlist, msg, {})
+ >>> sum(1 for mboxmsg in digest_mbox())
+ 0
+ >>> len(switchboard.files)
+ 2
+ >>> for filebase in switchboard.files:
+ ... qmsg, qdata = switchboard.dequeue(filebase)
+ ... switchboard.finish(filebase)
+ ... if qmsg.is_multipart():
+ ... mimemsg = qmsg
+ ... mimedata = qdata
+ ... else:
+ ... rfc1153msg = qmsg
+ ... rfc1153data = qdata
+ >>> print mimemsg.as_string()
+ Content-Type: multipart/mixed; boundary="..."
+ MIME-Version: 1.0
+ From: _xtest-request@example.com
+ Subject: XTest Digest, Vol 2, Issue 11
+ To: _xtest@example.com
+ Reply-To: _xtest@example.com
+ Date: ...
+ Message-ID: ...
+ <BLANKLINE>
+ --...
+ Content-Type: text/plain; charset="iso-8859-1"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+ Content-Description: XTest Digest, Vol 2, Issue 11
+ <BLANKLINE>
+ Envoyez vos messages pour la liste XTest =E0
+ _xtest@example.com
+ <BLANKLINE>
+ Pour vous (d=E9s)abonner par le web, consultez
+ http://www.example.com/listinfo/_xtest@example.com
+ <BLANKLINE>
+ ou, par courriel, envoyez un message avec =AB=A0help=A0=BB dans le corps ou
+ dans le sujet =E0
+ _xtest-request@example.com
+ <BLANKLINE>
+ Vous pouvez contacter l'administrateur de la liste =E0 l'adresse
+ _xtest-owner@example.com
+ <BLANKLINE>
+ Si vous r=E9pondez, n'oubliez pas de changer l'objet du message afin
+ qu'il soit plus sp=E9cifique que =AB=A0Re: Contenu du groupe de XTest...=A0=
+ =BB
+ <BLANKLINE>
+ --...
+ Content-Type: text/plain; charset="utf-8"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: base64
+ Content-Description: Today's Topics (1 messages)
+ <BLANKLINE>
+ VGjDqG1lcyBkdSBqb3VyIDoKCiAgIDEuIOS4gOeVqiAoYXBlcnNvbkBleGFtcGxlLm9yZykK
+ <BLANKLINE>
+ --...
+ Content-Type: multipart/digest; boundary="..."
+ MIME-Version: 1.0
+ <BLANKLINE>
+ --...
+ Content-Type: message/rfc822
+ MIME-Version: 1.0
+ <BLANKLINE>
+ Content-Transfer-Encoding: 7bit
+ From: aperson@example.org
+ MIME-Version: 1.0
+ To: _xtest@example.com
+ Content-Type: text/plain; charset=iso-2022-jp
+ Subject: =?iso-2022-jp?b?GyRCMGxIVhsoQg==?=
+ Message: 1
+ <BLANKLINE>
+ 一番
+ <BLANKLINE>
+ <BLANKLINE>
+ --...
+ --...
+ >>> sorted(mimedata.items())
+ [('_parsemsg', False),
+ ('isdigest', True),
+ ('listname', '_xtest@example.com'),
+ ('received_time', ...),
+ ('recips', set([])), ('version', 3)]
+ >>> print rfc1153msg.as_string()
+ From: _xtest-request@example.com
+ Subject: XTest Digest, Vol 2, Issue 11
+ To: _xtest@example.com
+ Reply-To: _xtest@example.com
+ Date: ...
+ Message-ID: ...
+ MIME-Version: 1.0
+ Content-Type: text/plain; charset="utf-8"
+ Content-Transfer-Encoding: base64
+ <BLANKLINE>
+ ...
+ <BLANKLINE>
+ >>> sorted(rfc1153data.items())
+ [('_parsemsg', False),
+ ('isdigest', True),
+ ('listname', '_xtest@example.com'),
+ ('received_time', ...),
+ ('recips', set([])), ('version', 3)]
diff --git a/Mailman/testing/test_documentation.py b/Mailman/testing/test_documentation.py
index 23641d57b..c5fb62302 100644
--- a/Mailman/testing/test_documentation.py
+++ b/Mailman/testing/test_documentation.py
@@ -60,15 +60,19 @@ def cleaning_teardown(testobj):
def test_suite():
suite = unittest.TestSuite()
docsdir = os.path.join(os.path.dirname(Mailman.__file__), 'docs')
+ # Under higher verbosity settings, report all doctest errors, not just the
+ # first one.
+ flags = (doctest.ELLIPSIS |
+ doctest.NORMALIZE_WHITESPACE |
+ doctest.REPORT_NDIFF)
+ if config.opts.verbosity <= 2:
+ flags |= doctest.REPORT_ONLY_FIRST_FAILURE
for filename in os.listdir(docsdir):
if os.path.splitext(filename)[1] == '.txt':
test = doctest.DocFileSuite(
'docs/' + filename,
package=Mailman,
- optionflags=(doctest.ELLIPSIS
- | doctest.NORMALIZE_WHITESPACE
- | doctest.REPORT_NDIFF
- | doctest.REPORT_ONLY_FIRST_FAILURE),
+ optionflags=flags,
tearDown=cleaning_teardown)
suite.addTest(test)
return suite
diff --git a/Mailman/testing/test_handlers.py b/Mailman/testing/test_handlers.py
index 8b1f11e98..aa7ac0dae 100644
--- a/Mailman/testing/test_handlers.py
+++ b/Mailman/testing/test_handlers.py
@@ -43,7 +43,6 @@ from Mailman.Handlers import Moderate
from Mailman.Handlers import Scrubber
# Don't test handlers such as SMTPDirect and Sendmail here
from Mailman.Handlers import ToArchive
-from Mailman.Handlers import ToDigest
@@ -275,124 +274,9 @@ It rocks!
-class TestToDigest(TestBase):
- def _makemsg(self, i=0):
- msg = email.message_from_string("""From: aperson@example.org
-To: _xtest@example.com
-Subject: message number %(i)d
-
-Here is message %(i)d
-""" % {'i' : i})
- return msg
-
- def setUp(self):
- TestBase.setUp(self)
- self._path = os.path.join(self._mlist.fullpath(), 'digest.mbox')
- fp = open(self._path, 'w')
- g = Generator(fp)
- for i in range(5):
- g.flatten(self._makemsg(i), unixfrom=1)
- fp.close()
- self._sb = Switchboard(config.VIRGINQUEUE_DIR)
-
- def tearDown(self):
- try:
- os.unlink(self._path)
- except OSError, e:
- if e.errno <> errno.ENOENT: raise
- for f in os.listdir(config.VIRGINQUEUE_DIR):
- os.unlink(os.path.join(config.VIRGINQUEUE_DIR, f))
- TestBase.tearDown(self)
-
- def test_short_circuit(self):
- eq = self.assertEqual
- mlist = self._mlist
- mlist.digestable = 0
- eq(ToDigest.process(mlist, None, {}), None)
- mlist.digestable = 1
- eq(ToDigest.process(mlist, None, {'isdigest': 1}), None)
- eq(self._sb.files(), [])
-
- def test_undersized(self):
- msg = self._makemsg(99)
- size = os.path.getsize(self._path) + len(str(msg))
- self._mlist.digest_size_threshhold = (size + 1) * 1024
- ToDigest.process(self._mlist, msg, {})
- self.assertEqual(self._sb.files(), [])
-
- def test_send_a_digest(self):
- eq = self.assertEqual
- mlist = self._mlist
- msg = self._makemsg(99)
- size = os.path.getsize(self._path) + len(str(msg))
- mlist.digest_size_threshhold = 0
- ToDigest.process(mlist, msg, {})
- files = self._sb.files()
- # There should be two files in the queue, one for the MIME digest and
- # one for the RFC 1153 digest.
- eq(len(files), 2)
- # Now figure out which of the two files is the MIME digest and which
- # is the RFC 1153 digest.
- for filebase in files:
- qmsg, qdata = self._sb.dequeue(filebase)
- if qmsg.get_content_maintype() == 'multipart':
- mimemsg = qmsg
- mimedata = qdata
- else:
- rfc1153msg = qmsg
- rfc1153data = qdata
- eq(rfc1153msg.get_content_type(), 'text/plain')
- eq(mimemsg.get_content_type(), 'multipart/mixed')
- eq(mimemsg['from'], mlist.GetRequestEmail())
- eq(mimemsg['subject'],
- '%(realname)s Digest, Vol %(volume)d, Issue %(issue)d' % {
- 'realname': mlist.real_name,
- 'volume' : mlist.volume,
- 'issue' : mlist.next_digest_number - 1,
- })
- eq(mimemsg['to'], mlist.GetListEmail())
- # BAW: this test is incomplete...
-
- def test_send_i18n_digest(self):
- eq = self.assertEqual
- mlist = self._mlist
- mlist.preferred_language = 'fr'
- msg = email.message_from_string("""\
-From: aperson@example.org
-To: _xtest@example.com
-Subject: =?iso-2022-jp?b?GyRCMGxIVhsoQg==?=
-MIME-Version: 1.0
-Content-Type: text/plain; charset=iso-2022-jp
-Content-Transfer-Encoding: 7bit
-
-\x1b$B0lHV\x1b(B
-""")
- mlist.digest_size_threshhold = 0
- ToDigest.process(mlist, msg, {})
- files = self._sb.files()
- eq(len(files), 2)
- for filebase in files:
- qmsg, qdata = self._sb.dequeue(filebase)
- if qmsg.get_content_maintype() == 'multipart':
- mimemsg = qmsg
- mimedata = qdata
- else:
- rfc1153msg = qmsg
- rfc1153data = qdata
- eq(rfc1153msg.get_content_type(), 'text/plain')
- eq(rfc1153msg.get_content_charset(), 'utf-8')
- eq(rfc1153msg['content-transfer-encoding'], 'base64')
- toc = mimemsg.get_payload()[1]
- eq(toc.get_content_type(), 'text/plain')
- eq(toc.get_content_charset(), 'utf-8')
- eq(toc['content-transfer-encoding'], 'base64')
-
-
-
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestApprove))
suite.addTest(unittest.makeSuite(TestScrubber))
suite.addTest(unittest.makeSuite(TestToArchive))
- suite.addTest(unittest.makeSuite(TestToDigest))
return suite