PageRenderTime 111ms CodeModel.GetById 58ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 1ms

/tests/regressiontests/mail/tests.py

https://code.google.com/p/mango-py/
Python | 636 lines | 526 code | 53 blank | 57 comment | 12 complexity | b7b9f39bda23742efbd09c1c0187628f MD5 | raw file
  1# coding: utf-8
  2import asyncore
  3import email
  4import os
  5import shutil
  6import smtpd
  7import sys
  8from StringIO import StringIO
  9import tempfile
 10import threading
 11
 12from django.conf import settings
 13from django.core import mail
 14from django.core.mail import (EmailMessage, mail_admins, mail_managers,
 15        EmailMultiAlternatives, send_mail, send_mass_mail)
 16from django.core.mail.backends import console, dummy, locmem, filebased, smtp
 17from django.core.mail.message import BadHeaderError
 18from django.test import TestCase
 19from django.utils.translation import ugettext_lazy
 20from django.utils.functional import wraps
 21
 22
 23def alter_django_settings(**kwargs):
 24    oldvalues = {}
 25    nonexistant = []
 26    for setting, newvalue in kwargs.iteritems():
 27        try:
 28            oldvalues[setting] = getattr(settings, setting)
 29        except AttributeError:
 30            nonexistant.append(setting)
 31        setattr(settings, setting, newvalue)
 32    return oldvalues, nonexistant
 33
 34
 35def restore_django_settings(state):
 36    oldvalues, nonexistant = state
 37    for setting, oldvalue in oldvalues.iteritems():
 38        setattr(settings, setting, oldvalue)
 39    for setting in nonexistant:
 40        delattr(settings, setting)
 41
 42
 43def with_django_settings(**kwargs):
 44    def decorator(test):
 45        @wraps(test)
 46        def decorated_test(self):
 47            state = alter_django_settings(**kwargs)
 48            try:
 49                return test(self)
 50            finally:
 51                restore_django_settings(state)
 52        return decorated_test
 53    return decorator
 54
 55
 56class MailTests(TestCase):
 57    """
 58    Non-backend specific tests.
 59    """
 60
 61    def test_ascii(self):
 62        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'])
 63        message = email.message()
 64        self.assertEqual(message['Subject'].encode(), 'Subject')
 65        self.assertEqual(message.get_payload(), 'Content')
 66        self.assertEqual(message['From'], 'from@example.com')
 67        self.assertEqual(message['To'], 'to@example.com')
 68
 69    def test_multiple_recipients(self):
 70        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'])
 71        message = email.message()
 72        self.assertEqual(message['Subject'].encode(), 'Subject')
 73        self.assertEqual(message.get_payload(), 'Content')
 74        self.assertEqual(message['From'], 'from@example.com')
 75        self.assertEqual(message['To'], 'to@example.com, other@example.com')
 76
 77    def test_cc(self):
 78        """Regression test for #7722"""
 79        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com'])
 80        message = email.message()
 81        self.assertEqual(message['Cc'], 'cc@example.com')
 82        self.assertEqual(email.recipients(), ['to@example.com', 'cc@example.com'])
 83
 84        # Test multiple CC with multiple To
 85        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'], cc=['cc@example.com', 'cc.other@example.com'])
 86        message = email.message()
 87        self.assertEqual(message['Cc'], 'cc@example.com, cc.other@example.com')
 88        self.assertEqual(email.recipients(), ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com'])
 89
 90        # Testing with Bcc
 91        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com', 'other@example.com'], cc=['cc@example.com', 'cc.other@example.com'], bcc=['bcc@example.com'])
 92        message = email.message()
 93        self.assertEqual(message['Cc'], 'cc@example.com, cc.other@example.com')
 94        self.assertEqual(email.recipients(), ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com', 'bcc@example.com'])
 95
 96    def test_header_injection(self):
 97        email = EmailMessage('Subject\nInjection Test', 'Content', 'from@example.com', ['to@example.com'])
 98        self.assertRaises(BadHeaderError, email.message)
 99        email = EmailMessage(ugettext_lazy('Subject\nInjection Test'), 'Content', 'from@example.com', ['to@example.com'])
100        self.assertRaises(BadHeaderError, email.message)
101
102    def test_space_continuation(self):
103        """
104        Test for space continuation character in long (ascii) subject headers (#7747)
105        """
106        email = EmailMessage('Long subject lines that get wrapped should use a space continuation character to get expected behaviour in Outlook and Thunderbird', 'Content', 'from@example.com', ['to@example.com'])
107        message = email.message()
108        self.assertEqual(message['Subject'], 'Long subject lines that get wrapped should use a space continuation\n character to get expected behaviour in Outlook and Thunderbird')
109
110    def test_message_header_overrides(self):
111        """
112        Specifying dates or message-ids in the extra headers overrides the
113        default values (#9233)
114        """
115        headers = {"date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
116        email = EmailMessage('subject', 'content', 'from@example.com', ['to@example.com'], headers=headers)
117        self.assertEqual(email.message().as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: subject\nFrom: from@example.com\nTo: to@example.com\ndate: Fri, 09 Nov 2001 01:08:47 -0000\nMessage-ID: foo\n\ncontent')
118
119    def test_from_header(self):
120        """
121        Make sure we can manually set the From header (#9214)
122        """
123        email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
124        message = email.message()
125        self.assertEqual(message['From'], 'from@example.com')
126
127    def test_multiple_message_call(self):
128        """
129        Regression for #13259 - Make sure that headers are not changed when
130        calling EmailMessage.message()
131        """
132        email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
133        message = email.message()
134        self.assertEqual(message['From'], 'from@example.com')
135        message = email.message()
136        self.assertEqual(message['From'], 'from@example.com')
137
138    def test_unicode_address_header(self):
139        """
140        Regression for #11144 - When a to/from/cc header contains unicode,
141        make sure the email addresses are parsed correctly (especially with
142        regards to commas)
143        """
144        email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Firstname Sürname" <to@example.com>', 'other@example.com'])
145        self.assertEqual(email.message()['To'], '=?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com')
146        email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>', 'other@example.com'])
147        self.assertEqual(email.message()['To'], '=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com')
148
149    def test_unicode_headers(self):
150        email = EmailMessage(u"G?eg?ó?ka", "Content", "from@example.com", ["to@example.com"],
151                             headers={"Sender": '"Firstname Sürname" <sender@example.com>',
152                                      "Comments": 'My Sürname is non-ASCII'})
153        message = email.message()
154        self.assertEqual(message['Subject'], '=?utf-8?b?R8W8ZWfFvMOzxYJrYQ==?=')
155        self.assertEqual(message['Sender'], '=?utf-8?q?Firstname_S=C3=BCrname?= <sender@example.com>')
156        self.assertEqual(message['Comments'], '=?utf-8?q?My_S=C3=BCrname_is_non-ASCII?=')
157
158    def test_safe_mime_multipart(self):
159        """
160        Make sure headers can be set with a different encoding than utf-8 in
161        SafeMIMEMultipart as well
162        """
163        headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
164        subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>'
165        text_content = 'This is an important message.'
166        html_content = '<p>This is an <strong>important</strong> message.</p>'
167        msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers)
168        msg.attach_alternative(html_content, "text/html")
169        msg.encoding = 'iso-8859-1'
170        self.assertEqual(msg.message()['To'], '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>')
171        self.assertEqual(msg.message()['Subject'].encode(), u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=')
172
173    def test_encoding(self):
174        """
175        Regression for #12791 - Encode body correctly with other encodings
176        than utf-8
177        """
178        email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com'])
179        email.encoding = 'iso-8859-1'
180        message = email.message()
181        self.assertTrue(message.as_string().startswith('Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: other@example.com'))
182        self.assertEqual(message.get_payload(), 'Firstname S=FCrname is a great guy.')
183
184        # Make sure MIME attachments also works correctly with other encodings than utf-8
185        text_content = 'Firstname Sürname is a great guy.'
186        html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>'
187        msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com'])
188        msg.encoding = 'iso-8859-1'
189        msg.attach_alternative(html_content, "text/html")
190        self.assertEqual(msg.message().get_payload(0).as_string(), 'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.')
191        self.assertEqual(msg.message().get_payload(1).as_string(), 'Content-Type: text/html; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>')
192
193    def test_attachments(self):
194        """Regression test for #9367"""
195        headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
196        subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
197        text_content = 'This is an important message.'
198        html_content = '<p>This is an <strong>important</strong> message.</p>'
199        msg = EmailMultiAlternatives(subject, text_content, from_email, [to], headers=headers)
200        msg.attach_alternative(html_content, "text/html")
201        msg.attach("an attachment.pdf", "%PDF-1.4.%...", mimetype="application/pdf")
202        msg_str = msg.message().as_string()
203        message = email.message_from_string(msg_str)
204        self.assertTrue(message.is_multipart())
205        self.assertEqual(message.get_content_type(), 'multipart/mixed')
206        self.assertEqual(message.get_default_type(), 'text/plain')
207        payload = message.get_payload()
208        self.assertEqual(payload[0].get_content_type(), 'multipart/alternative')
209        self.assertEqual(payload[1].get_content_type(), 'application/pdf')
210
211    def test_dummy_backend(self):
212        """
213        Make sure that dummy backends returns correct number of sent messages
214        """
215        connection = dummy.EmailBackend()
216        email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
217        self.assertEqual(connection.send_messages([email, email, email]), 3)
218
219    def test_arbitrary_keyword(self):
220        """
221        Make sure that get_connection() accepts arbitrary keyword that might be
222        used with custom backends.
223        """
224        c = mail.get_connection(fail_silently=True, foo='bar')
225        self.assertTrue(c.fail_silently)
226
227    def test_custom_backend(self):
228        """Test custom backend defined in this suite."""
229        conn = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
230        self.assertTrue(hasattr(conn, 'test_outbox'))
231        email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
232        conn.send_messages([email])
233        self.assertEqual(len(conn.test_outbox), 1)
234
235    def test_backend_arg(self):
236        """Test backend argument of mail.get_connection()"""
237        self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.smtp.EmailBackend'), smtp.EmailBackend))
238        self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.locmem.EmailBackend'), locmem.EmailBackend))
239        self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.dummy.EmailBackend'), dummy.EmailBackend))
240        self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.console.EmailBackend'), console.EmailBackend))
241        tmp_dir = tempfile.mkdtemp()
242        try:
243            self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.filebased.EmailBackend', file_path=tmp_dir), filebased.EmailBackend))
244        finally:
245            shutil.rmtree(tmp_dir)
246        self.assertTrue(isinstance(mail.get_connection(), locmem.EmailBackend))
247
248    @with_django_settings(
249        EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend',
250        ADMINS=[('nobody', 'nobody@example.com')],
251        MANAGERS=[('nobody', 'nobody@example.com')])
252    def test_connection_arg(self):
253        """Test connection argument to send_mail(), et. al."""
254        mail.outbox = []
255
256        # Send using non-default connection
257        connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
258        send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
259        self.assertEqual(mail.outbox, [])
260        self.assertEqual(len(connection.test_outbox), 1)
261        self.assertEqual(connection.test_outbox[0].subject, 'Subject')
262
263        connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
264        send_mass_mail([
265                ('Subject1', 'Content1', 'from1@example.com', ['to1@example.com']),
266                ('Subject2', 'Content2', 'from2@example.com', ['to2@example.com']),
267            ], connection=connection)
268        self.assertEqual(mail.outbox, [])
269        self.assertEqual(len(connection.test_outbox), 2)
270        self.assertEqual(connection.test_outbox[0].subject, 'Subject1')
271        self.assertEqual(connection.test_outbox[1].subject, 'Subject2')
272
273        connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
274        mail_admins('Admin message', 'Content', connection=connection)
275        self.assertEqual(mail.outbox, [])
276        self.assertEqual(len(connection.test_outbox), 1)
277        self.assertEqual(connection.test_outbox[0].subject, '[Django] Admin message')
278
279        connection = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
280        mail_managers('Manager message', 'Content', connection=connection)
281        self.assertEqual(mail.outbox, [])
282        self.assertEqual(len(connection.test_outbox), 1)
283        self.assertEqual(connection.test_outbox[0].subject, '[Django] Manager message')
284
285    def test_dont_mangle_from_in_body(self):
286        # Regression for #13433 - Make sure that EmailMessage doesn't mangle
287        # 'From ' in message body.
288        email = EmailMessage('Subject', 'From the future', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
289        self.assertFalse('>From the future' in email.message().as_string())
290
291
292class BaseEmailBackendTests(object):
293    email_backend = None
294
295    def setUp(self):
296        self.__settings_state = alter_django_settings(EMAIL_BACKEND=self.email_backend)
297
298    def tearDown(self):
299        restore_django_settings(self.__settings_state)
300
301    def assertStartsWith(self, first, second):
302        if not first.startswith(second):
303            self.longMessage = True
304            self.assertEqual(first[:len(second)], second, "First string doesn't start with the second.")
305
306    def get_mailbox_content(self):
307        raise NotImplementedError
308
309    def flush_mailbox(self):
310        raise NotImplementedError
311
312    def get_the_message(self):
313        mailbox = self.get_mailbox_content()
314        self.assertEqual(len(mailbox), 1,
315            "Expected exactly one message, got %d.\n%r" % (len(mailbox), [
316                m.as_string() for m in mailbox]))
317        return mailbox[0]
318
319    def test_send(self):
320        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'])
321        num_sent = mail.get_connection().send_messages([email])
322        self.assertEqual(num_sent, 1)
323        message = self.get_the_message()
324        self.assertEqual(message["subject"], "Subject")
325        self.assertEqual(message.get_payload(), "Content")
326        self.assertEqual(message["from"], "from@example.com")
327        self.assertEqual(message.get_all("to"), ["to@example.com"])
328
329    def test_send_many(self):
330        email1 = EmailMessage('Subject', 'Content1', 'from@example.com', ['to@example.com'])
331        email2 = EmailMessage('Subject', 'Content2', 'from@example.com', ['to@example.com'])
332        num_sent = mail.get_connection().send_messages([email1, email2])
333        self.assertEqual(num_sent, 2)
334        messages = self.get_mailbox_content()
335        self.assertEqual(len(messages), 2)
336        self.assertEqual(messages[0].get_payload(), "Content1")
337        self.assertEqual(messages[1].get_payload(), "Content2")
338
339    def test_send_verbose_name(self):
340        email = EmailMessage("Subject", "Content", '"Firstname Sürname" <from@example.com>',
341                             ["to@example.com"])
342        email.send()
343        message = self.get_the_message()
344        self.assertEqual(message["subject"], "Subject")
345        self.assertEqual(message.get_payload(), "Content")
346        self.assertEqual(message["from"], "=?utf-8?q?Firstname_S=C3=BCrname?= <from@example.com>")
347
348    @with_django_settings(MANAGERS=[('nobody', 'nobody@example.com')])
349    def test_html_mail_managers(self):
350        """Test html_message argument to mail_managers"""
351        mail_managers('Subject', 'Content', html_message='HTML Content')
352        message = self.get_the_message()
353
354        self.assertEqual(message.get('subject'), '[Django] Subject')
355        self.assertEqual(message.get_all('to'), ['nobody@example.com'])
356        self.assertTrue(message.is_multipart())
357        self.assertEqual(len(message.get_payload()), 2)
358        self.assertEqual(message.get_payload(0).get_payload(), 'Content')
359        self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain')
360        self.assertEqual(message.get_payload(1).get_payload(), 'HTML Content')
361        self.assertEqual(message.get_payload(1).get_content_type(), 'text/html')
362
363    @with_django_settings(ADMINS=[('nobody', 'nobody@example.com')])
364    def test_html_mail_admins(self):
365        """Test html_message argument to mail_admins """
366        mail_admins('Subject', 'Content', html_message='HTML Content')
367        message = self.get_the_message()
368
369        self.assertEqual(message.get('subject'), '[Django] Subject')
370        self.assertEqual(message.get_all('to'), ['nobody@example.com'])
371        self.assertTrue(message.is_multipart())
372        self.assertEqual(len(message.get_payload()), 2)
373        self.assertEqual(message.get_payload(0).get_payload(), 'Content')
374        self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain')
375        self.assertEqual(message.get_payload(1).get_payload(), 'HTML Content')
376        self.assertEqual(message.get_payload(1).get_content_type(), 'text/html')
377
378    @with_django_settings(ADMINS=[('nobody', 'nobody+admin@example.com')],
379                         MANAGERS=[('nobody', 'nobody+manager@example.com')])
380    def test_manager_and_admin_mail_prefix(self):
381        """
382        String prefix + lazy translated subject = bad output
383        Regression for #13494
384        """
385        mail_managers(ugettext_lazy('Subject'), 'Content')
386        message = self.get_the_message()
387        self.assertEqual(message.get('subject'), '[Django] Subject')
388
389        self.flush_mailbox()
390        mail_admins(ugettext_lazy('Subject'), 'Content')
391        message = self.get_the_message()
392        self.assertEqual(message.get('subject'), '[Django] Subject')
393
394    @with_django_settings(ADMINS=(), MANAGERS=())
395    def test_empty_admins(self):
396        """
397        Test that mail_admins/mail_managers doesn't connect to the mail server
398        if there are no recipients (#9383)
399        """
400        mail_admins('hi', 'there')
401        self.assertEqual(self.get_mailbox_content(), [])
402        mail_managers('hi', 'there')
403        self.assertEqual(self.get_mailbox_content(), [])
404
405    def test_message_cc_header(self):
406        """
407        Regression test for #7722
408        """
409        email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com'])
410        mail.get_connection().send_messages([email])
411        message = self.get_the_message()
412        self.assertStartsWith(message.as_string(), 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nCc: cc@example.com\nDate: ')
413
414    def test_idn_send(self):
415        """
416        Regression test for #14301
417        """
418        self.assertTrue(send_mail('Subject', 'Content', 'from@öäü.com', [u'to@öäü.com']))
419        message = self.get_the_message()
420        self.assertEqual(message.get('subject'), 'Subject')
421        self.assertEqual(message.get('from'), 'from@xn--4ca9at.com')
422        self.assertEqual(message.get('to'), 'to@xn--4ca9at.com')
423
424        self.flush_mailbox()
425        m = EmailMessage('Subject', 'Content', 'from@öäü.com',
426                     [u'to@öäü.com'], cc=[u'cc@öäü.com'])
427        m.send()
428        message = self.get_the_message()
429        self.assertEqual(message.get('subject'), 'Subject')
430        self.assertEqual(message.get('from'), 'from@xn--4ca9at.com')
431        self.assertEqual(message.get('to'), 'to@xn--4ca9at.com')
432        self.assertEqual(message.get('cc'), 'cc@xn--4ca9at.com')
433
434    def test_recipient_without_domain(self):
435        """
436        Regression test for #15042
437        """
438        self.assertTrue(send_mail("Subject", "Content", "tester", ["django"]))
439        message = self.get_the_message()
440        self.assertEqual(message.get('subject'), 'Subject')
441        self.assertEqual(message.get('from'), "tester")
442        self.assertEqual(message.get('to'), "django")
443
444
445class LocmemBackendTests(BaseEmailBackendTests, TestCase):
446    email_backend = 'django.core.mail.backends.locmem.EmailBackend'
447
448    def get_mailbox_content(self):
449        return [m.message() for m in mail.outbox]
450
451    def flush_mailbox(self):
452        mail.outbox = []
453
454    def tearDown(self):
455        super(LocmemBackendTests, self).tearDown()
456        mail.outbox = []
457
458    def test_locmem_shared_messages(self):
459        """
460        Make sure that the locmen backend populates the outbox.
461        """
462        connection = locmem.EmailBackend()
463        connection2 = locmem.EmailBackend()
464        email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
465        connection.send_messages([email])
466        connection2.send_messages([email])
467        self.assertEqual(len(mail.outbox), 2)
468
469
470class FileBackendTests(BaseEmailBackendTests, TestCase):
471    email_backend = 'django.core.mail.backends.filebased.EmailBackend'
472
473    def setUp(self):
474        super(FileBackendTests, self).setUp()
475        self.tmp_dir = tempfile.mkdtemp()
476        self.__settings_state = alter_django_settings(EMAIL_FILE_PATH=self.tmp_dir)
477
478    def tearDown(self):
479        restore_django_settings(self.__settings_state)
480        shutil.rmtree(self.tmp_dir)
481        super(FileBackendTests, self).tearDown()
482
483    def flush_mailbox(self):
484        for filename in os.listdir(self.tmp_dir):
485            os.unlink(os.path.join(self.tmp_dir, filename))
486
487    def get_mailbox_content(self):
488        messages = []
489        for filename in os.listdir(self.tmp_dir):
490            session = open(os.path.join(self.tmp_dir, filename)).read().split('\n' + ('-' * 79) + '\n')
491            messages.extend(email.message_from_string(m) for m in session if m)
492        return messages
493
494    def test_file_sessions(self):
495        """Make sure opening a connection creates a new file"""
496        msg = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
497        connection = mail.get_connection()
498        connection.send_messages([msg])
499
500        self.assertEqual(len(os.listdir(self.tmp_dir)), 1)
501        message = email.message_from_file(open(os.path.join(self.tmp_dir, os.listdir(self.tmp_dir)[0])))
502        self.assertEqual(message.get_content_type(), 'text/plain')
503        self.assertEqual(message.get('subject'), 'Subject')
504        self.assertEqual(message.get('from'), 'from@example.com')
505        self.assertEqual(message.get('to'), 'to@example.com')
506
507        connection2 = mail.get_connection()
508        connection2.send_messages([msg])
509        self.assertEqual(len(os.listdir(self.tmp_dir)), 2)
510
511        connection.send_messages([msg])
512        self.assertEqual(len(os.listdir(self.tmp_dir)), 2)
513
514        msg.connection = mail.get_connection()
515        self.assertTrue(connection.open())
516        msg.send()
517        self.assertEqual(len(os.listdir(self.tmp_dir)), 3)
518        msg.send()
519        self.assertEqual(len(os.listdir(self.tmp_dir)), 3)
520
521
522class ConsoleBackendTests(BaseEmailBackendTests, TestCase):
523    email_backend = 'django.core.mail.backends.console.EmailBackend'
524
525    def setUp(self):
526        super(ConsoleBackendTests, self).setUp()
527        self.__stdout = sys.stdout
528        self.stream = sys.stdout = StringIO()
529
530    def tearDown(self):
531        del self.stream
532        sys.stdout = self.__stdout
533        del self.__stdout
534        super(ConsoleBackendTests, self).tearDown()
535
536    def flush_mailbox(self):
537        self.stream = sys.stdout = StringIO()
538
539    def get_mailbox_content(self):
540        messages = self.stream.getvalue().split('\n' + ('-' * 79) + '\n')
541        return [email.message_from_string(m) for m in messages if m]
542
543    def test_console_stream_kwarg(self):
544        """
545        Test that the console backend can be pointed at an arbitrary stream.
546        """
547        s = StringIO()
548        connection = mail.get_connection('django.core.mail.backends.console.EmailBackend', stream=s)
549        send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
550        self.assertTrue(s.getvalue().startswith('Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nDate: '))
551
552
553class FakeSMTPServer(smtpd.SMTPServer, threading.Thread):
554    """
555    Asyncore SMTP server wrapped into a thread. Based on DummyFTPServer from:
556    http://svn.python.org/view/python/branches/py3k/Lib/test/test_ftplib.py?revision=86061&view=markup
557    """
558
559    def __init__(self, *args, **kwargs):
560        threading.Thread.__init__(self)
561        smtpd.SMTPServer.__init__(self, *args, **kwargs)
562        self._sink = []
563        self.active = False
564        self.active_lock = threading.Lock()
565        self.sink_lock = threading.Lock()
566
567    def process_message(self, peer, mailfrom, rcpttos, data):
568        m = email.message_from_string(data)
569        maddr = email.Utils.parseaddr(m.get('from'))[1]
570        if mailfrom != maddr:
571            return "553 '%s' != '%s'" % (mailfrom, maddr)
572        self.sink_lock.acquire()
573        self._sink.append(m)
574        self.sink_lock.release()
575
576    def get_sink(self):
577        self.sink_lock.acquire()
578        try:
579            return self._sink[:]
580        finally:
581            self.sink_lock.release()
582
583    def flush_sink(self):
584        self.sink_lock.acquire()
585        self._sink[:] = []
586        self.sink_lock.release()
587
588    def start(self):
589        assert not self.active
590        self.__flag = threading.Event()
591        threading.Thread.start(self)
592        self.__flag.wait()
593
594    def run(self):
595        self.active = True
596        self.__flag.set()
597        while self.active and asyncore.socket_map:
598            self.active_lock.acquire()
599            asyncore.loop(timeout=0.1, count=1)
600            self.active_lock.release()
601        asyncore.close_all()
602
603    def stop(self):
604        assert self.active
605        self.active = False
606        self.join()
607
608
609class SMTPBackendTests(BaseEmailBackendTests, TestCase):
610    email_backend = 'django.core.mail.backends.smtp.EmailBackend'
611
612    @classmethod
613    def setUpClass(cls):
614        cls.server = FakeSMTPServer(('127.0.0.1', 0), None)
615        cls.settings = alter_django_settings(
616            EMAIL_HOST="127.0.0.1",
617            EMAIL_PORT=cls.server.socket.getsockname()[1])
618        cls.server.start()
619
620    @classmethod
621    def tearDownClass(cls):
622        cls.server.stop()
623
624    def setUp(self):
625        super(SMTPBackendTests, self).setUp()
626        self.server.flush_sink()
627
628    def tearDown(self):
629        self.server.flush_sink()
630        super(SMTPBackendTests, self).tearDown()
631
632    def flush_mailbox(self):
633        self.server.flush_sink()
634
635    def get_mailbox_content(self):
636        return self.server.get_sink()