blob: b2dfce444371f1e8755786d660668219a0641256 [file] [log] [blame]
Barry Warsaw41075852001-09-23 03:18:13 +00001# Copyright (C) 2001 Python Software Foundation
2# email package unit tests
3
4import os
5import time
6import unittest
7import base64
8from cStringIO import StringIO
9from types import StringType
10
11import email
12
Barry Warsawbf7a59d2001-10-11 15:44:50 +000013from email.Parser import Parser, HeaderParser
Barry Warsaw41075852001-09-23 03:18:13 +000014from email.Generator import Generator, DecodedGenerator
15from email.Message import Message
Barry Warsawfee435a2001-10-09 19:23:57 +000016from email.MIMEAudio import MIMEAudio
Barry Warsaw65279d02001-09-26 05:47:08 +000017from email.MIMEText import MIMEText
18from email.MIMEImage import MIMEImage
Barry Warsaw41075852001-09-23 03:18:13 +000019from email.MIMEBase import MIMEBase
Barry Warsaw65279d02001-09-26 05:47:08 +000020from email.MIMEMessage import MIMEMessage
Barry Warsaw41075852001-09-23 03:18:13 +000021from email import Utils
22from email import Errors
23from email import Encoders
24from email import Iterators
25
Barry Warsawfee435a2001-10-09 19:23:57 +000026import test_email
27from test_support import findfile
Barry Warsaw41075852001-09-23 03:18:13 +000028
Barry Warsaw1f0fa922001-10-19 04:08:59 +000029
Barry Warsaw41075852001-09-23 03:18:13 +000030NL = '\n'
31EMPTYSTRING = ''
Barry Warsaw07227d12001-10-17 20:52:26 +000032SPACE = ' '
Barry Warsaw41075852001-09-23 03:18:13 +000033
34
Barry Warsaw08a534d2001-10-04 17:58:50 +000035
Barry Warsaw41075852001-09-23 03:18:13 +000036def openfile(filename):
Barry Warsawfee435a2001-10-09 19:23:57 +000037 path = os.path.join(os.path.dirname(test_email.__file__), 'data', filename)
Barry Warsaw41075852001-09-23 03:18:13 +000038 return open(path)
39
40
Barry Warsaw08a534d2001-10-04 17:58:50 +000041
Barry Warsaw41075852001-09-23 03:18:13 +000042# Base test class
43class TestEmailBase(unittest.TestCase):
44 def _msgobj(self, filename):
45 fp = openfile(filename)
46 try:
Barry Warsaw65279d02001-09-26 05:47:08 +000047 msg = email.message_from_file(fp)
Barry Warsaw41075852001-09-23 03:18:13 +000048 finally:
49 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +000050 return msg
Barry Warsaw41075852001-09-23 03:18:13 +000051
52
Barry Warsaw08a534d2001-10-04 17:58:50 +000053
Barry Warsaw41075852001-09-23 03:18:13 +000054# Test various aspects of the Message class's API
55class TestMessageAPI(TestEmailBase):
Barry Warsaw2f6a0b02001-10-09 15:49:35 +000056 def test_get_all(self):
57 eq = self.assertEqual
58 msg = self._msgobj('msg_20.txt')
59 eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
60 eq(msg.get_all('xx', 'n/a'), 'n/a')
61
Barry Warsaw41075852001-09-23 03:18:13 +000062 def test_get_charsets(self):
63 eq = self.assertEqual
Tim Peters527e64f2001-10-04 05:36:56 +000064
Barry Warsaw65279d02001-09-26 05:47:08 +000065 msg = self._msgobj('msg_08.txt')
66 charsets = msg.get_charsets()
67 eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000068
Barry Warsaw65279d02001-09-26 05:47:08 +000069 msg = self._msgobj('msg_09.txt')
70 charsets = msg.get_charsets('dingbat')
71 eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
72 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000073
Barry Warsaw65279d02001-09-26 05:47:08 +000074 msg = self._msgobj('msg_12.txt')
75 charsets = msg.get_charsets()
76 eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
77 'iso-8859-3', 'us-ascii', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000078
79 def test_get_filename(self):
80 eq = self.assertEqual
81
Barry Warsaw65279d02001-09-26 05:47:08 +000082 msg = self._msgobj('msg_04.txt')
83 filenames = [p.get_filename() for p in msg.get_payload()]
Barry Warsaw41075852001-09-23 03:18:13 +000084 eq(filenames, ['msg.txt', 'msg.txt'])
85
Barry Warsaw65279d02001-09-26 05:47:08 +000086 msg = self._msgobj('msg_07.txt')
87 subpart = msg.get_payload(1)
Barry Warsaw41075852001-09-23 03:18:13 +000088 eq(subpart.get_filename(), 'dingusfish.gif')
89
90 def test_get_boundary(self):
91 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +000092 msg = self._msgobj('msg_07.txt')
Barry Warsaw41075852001-09-23 03:18:13 +000093 # No quotes!
Barry Warsaw65279d02001-09-26 05:47:08 +000094 eq(msg.get_boundary(), 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +000095
96 def test_set_boundary(self):
97 eq = self.assertEqual
98 # This one has no existing boundary parameter, but the Content-Type:
99 # header appears fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000100 msg = self._msgobj('msg_01.txt')
101 msg.set_boundary('BOUNDARY')
102 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000103 eq(header.lower(), 'content-type')
104 eq(value, 'text/plain; charset=us-ascii; boundary="BOUNDARY"')
105 # This one has a Content-Type: header, with a boundary, stuck in the
106 # middle of its headers. Make sure the order is preserved; it should
107 # be fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000108 msg = self._msgobj('msg_04.txt')
109 msg.set_boundary('BOUNDARY')
110 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000111 eq(header.lower(), 'content-type')
112 eq(value, 'multipart/mixed; boundary="BOUNDARY"')
113 # And this one has no Content-Type: header at all.
Barry Warsaw65279d02001-09-26 05:47:08 +0000114 msg = self._msgobj('msg_03.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000115 self.assertRaises(Errors.HeaderParseError,
Barry Warsaw65279d02001-09-26 05:47:08 +0000116 msg.set_boundary, 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000117
118 def test_get_decoded_payload(self):
119 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000120 msg = self._msgobj('msg_10.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000121 # The outer message is a multipart
Barry Warsaw65279d02001-09-26 05:47:08 +0000122 eq(msg.get_payload(decode=1), None)
Barry Warsaw41075852001-09-23 03:18:13 +0000123 # Subpart 1 is 7bit encoded
Barry Warsaw65279d02001-09-26 05:47:08 +0000124 eq(msg.get_payload(0).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000125 'This is a 7bit encoded message.\n')
126 # Subpart 2 is quopri
Barry Warsaw65279d02001-09-26 05:47:08 +0000127 eq(msg.get_payload(1).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000128 '\xa1This is a Quoted Printable encoded message!\n')
129 # Subpart 3 is base64
Barry Warsaw65279d02001-09-26 05:47:08 +0000130 eq(msg.get_payload(2).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000131 'This is a Base64 encoded message.')
132 # Subpart 4 has no Content-Transfer-Encoding: header.
Barry Warsaw65279d02001-09-26 05:47:08 +0000133 eq(msg.get_payload(3).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000134 'This has no Content-Transfer-Encoding: header.\n')
135
136 def test_decoded_generator(self):
137 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000138 msg = self._msgobj('msg_07.txt')
139 fp = openfile('msg_17.txt')
140 try:
141 text = fp.read()
142 finally:
143 fp.close()
Barry Warsaw41075852001-09-23 03:18:13 +0000144 s = StringIO()
145 g = DecodedGenerator(s)
Barry Warsaw65279d02001-09-26 05:47:08 +0000146 g(msg)
147 eq(s.getvalue(), text)
Barry Warsaw41075852001-09-23 03:18:13 +0000148
149 def test__contains__(self):
150 msg = Message()
151 msg['From'] = 'Me'
152 msg['to'] = 'You'
153 # Check for case insensitivity
154 self.failUnless('from' in msg)
155 self.failUnless('From' in msg)
156 self.failUnless('FROM' in msg)
157 self.failUnless('to' in msg)
158 self.failUnless('To' in msg)
159 self.failUnless('TO' in msg)
160
161 def test_as_string(self):
162 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000163 msg = self._msgobj('msg_01.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000164 fp = openfile('msg_01.txt')
165 try:
166 text = fp.read()
167 finally:
168 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000169 eq(text, msg.as_string())
170 fullrepr = str(msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000171 lines = fullrepr.split('\n')
172 self.failUnless(lines[0].startswith('From '))
173 eq(text, NL.join(lines[1:]))
174
175 def test_bad_param(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000176 msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000177 self.assertEqual(msg.get_param('baz'), '')
178
179 def test_missing_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000180 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000181 self.assertEqual(msg.get_filename(), None)
182
183 def test_bogus_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000184 msg = email.message_from_string(
185 "Content-Disposition: blarg; filename\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000186 self.assertEqual(msg.get_filename(), '')
Tim Peters527e64f2001-10-04 05:36:56 +0000187
Barry Warsaw41075852001-09-23 03:18:13 +0000188 def test_missing_boundary(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000189 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000190 self.assertEqual(msg.get_boundary(), None)
191
Barry Warsaw65279d02001-09-26 05:47:08 +0000192 def test_get_params(self):
193 eq = self.assertEqual
194 msg = email.message_from_string(
195 'X-Header: foo=one; bar=two; baz=three\n')
196 eq(msg.get_params(header='x-header'),
197 [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
198 msg = email.message_from_string(
199 'X-Header: foo; bar=one; baz=two\n')
200 eq(msg.get_params(header='x-header'),
201 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
202 eq(msg.get_params(), None)
203 msg = email.message_from_string(
204 'X-Header: foo; bar="one"; baz=two\n')
205 eq(msg.get_params(header='x-header'),
206 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
207
208 def test_get_param(self):
209 eq = self.assertEqual
210 msg = email.message_from_string(
211 "X-Header: foo=one; bar=two; baz=three\n")
212 eq(msg.get_param('bar', header='x-header'), 'two')
213 eq(msg.get_param('quuz', header='x-header'), None)
214 eq(msg.get_param('quuz'), None)
215 msg = email.message_from_string(
216 'X-Header: foo; bar="one"; baz=two\n')
217 eq(msg.get_param('foo', header='x-header'), '')
218 eq(msg.get_param('bar', header='x-header'), 'one')
219 eq(msg.get_param('baz', header='x-header'), 'two')
220
221 def test_has_key(self):
222 msg = email.message_from_string('Header: exists')
223 self.failUnless(msg.has_key('header'))
224 self.failUnless(msg.has_key('Header'))
225 self.failUnless(msg.has_key('HEADER'))
226 self.failIf(msg.has_key('headeri'))
227
Barry Warsaw41075852001-09-23 03:18:13 +0000228
Barry Warsaw08a534d2001-10-04 17:58:50 +0000229
Barry Warsaw41075852001-09-23 03:18:13 +0000230# Test the email.Encoders module
231class TestEncoders(unittest.TestCase):
232 def test_encode_noop(self):
233 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000234 msg = MIMEText('hello world', _encoder=Encoders.encode_noop)
Barry Warsaw41075852001-09-23 03:18:13 +0000235 eq(msg.get_payload(), 'hello world\n')
236 eq(msg['content-transfer-encoding'], None)
237
238 def test_encode_7bit(self):
239 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000240 msg = MIMEText('hello world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000241 eq(msg.get_payload(), 'hello world\n')
242 eq(msg['content-transfer-encoding'], '7bit')
Barry Warsaw65279d02001-09-26 05:47:08 +0000243 msg = MIMEText('hello \x7f world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000244 eq(msg.get_payload(), 'hello \x7f world\n')
245 eq(msg['content-transfer-encoding'], '7bit')
246
247 def test_encode_8bit(self):
248 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000249 msg = MIMEText('hello \x80 world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000250 eq(msg.get_payload(), 'hello \x80 world\n')
251 eq(msg['content-transfer-encoding'], '8bit')
252
253 def test_encode_base64(self):
254 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000255 msg = MIMEText('hello world', _encoder=Encoders.encode_base64)
Barry Warsaw41075852001-09-23 03:18:13 +0000256 eq(msg.get_payload(), 'aGVsbG8gd29ybGQK\n')
257 eq(msg['content-transfer-encoding'], 'base64')
258
259 def test_encode_quoted_printable(self):
260 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000261 msg = MIMEText('hello world', _encoder=Encoders.encode_quopri)
Barry Warsaw41075852001-09-23 03:18:13 +0000262 eq(msg.get_payload(), 'hello=20world\n')
263 eq(msg['content-transfer-encoding'], 'quoted-printable')
264
265
Barry Warsaw08a534d2001-10-04 17:58:50 +0000266
267# Test long header wrapping
Barry Warsaw41075852001-09-23 03:18:13 +0000268class TestLongHeaders(unittest.TestCase):
269 def test_header_splitter(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000270 msg = MIMEText('')
Barry Warsaw41075852001-09-23 03:18:13 +0000271 # It'd be great if we could use add_header() here, but that doesn't
272 # guarantee an order of the parameters.
273 msg['X-Foobar-Spoink-Defrobnit'] = (
274 'wasnipoop; giraffes="very-long-necked-animals"; '
275 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
276 sfp = StringIO()
277 g = Generator(sfp)
278 g(msg)
Barry Warsaw08a534d2001-10-04 17:58:50 +0000279 self.assertEqual(sfp.getvalue(), openfile('msg_18.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +0000280
Barry Warsaw07227d12001-10-17 20:52:26 +0000281 def test_no_semis_header_splitter(self):
282 msg = Message()
283 msg['From'] = 'test@dom.ain'
284 refparts = []
285 for i in range(10):
286 refparts.append('<%d@dom.ain>' % i)
287 msg['References'] = SPACE.join(refparts)
288 msg.set_payload('Test')
289 sfp = StringIO()
290 g = Generator(sfp)
291 g(msg)
292 self.assertEqual(sfp.getvalue(), """\
293From: test@dom.ain
294References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
Tim Peterse0c446b2001-10-18 21:57:37 +0000295\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
Barry Warsaw07227d12001-10-17 20:52:26 +0000296
297Test""")
298
299 def test_no_split_long_header(self):
300 msg = Message()
301 msg['From'] = 'test@dom.ain'
302 refparts = []
303 msg['References'] = 'x' * 80
304 msg.set_payload('Test')
305 sfp = StringIO()
306 g = Generator(sfp)
307 g(msg)
308 self.assertEqual(sfp.getvalue(), """\
309From: test@dom.ain
310References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
311
312Test""")
313
Barry Warsaw41075852001-09-23 03:18:13 +0000314
Barry Warsaw08a534d2001-10-04 17:58:50 +0000315
316# Test mangling of "From " lines in the body of a message
Barry Warsaw41075852001-09-23 03:18:13 +0000317class TestFromMangling(unittest.TestCase):
318 def setUp(self):
319 self.msg = Message()
320 self.msg['From'] = 'aaa@bbb.org'
321 self.msg.add_payload("""\
322From the desk of A.A.A.:
323Blah blah blah
324""")
325
326 def test_mangled_from(self):
327 s = StringIO()
328 g = Generator(s, mangle_from_=1)
329 g(self.msg)
330 self.assertEqual(s.getvalue(), """\
331From: aaa@bbb.org
332
333>From the desk of A.A.A.:
334Blah blah blah
335""")
336
337 def test_dont_mangle_from(self):
338 s = StringIO()
339 g = Generator(s, mangle_from_=0)
340 g(self.msg)
341 self.assertEqual(s.getvalue(), """\
342From: aaa@bbb.org
343
344From the desk of A.A.A.:
345Blah blah blah
346""")
347
348
Barry Warsaw08a534d2001-10-04 17:58:50 +0000349
Barry Warsawfee435a2001-10-09 19:23:57 +0000350# Test the basic MIMEAudio class
351class TestMIMEAudio(unittest.TestCase):
352 def setUp(self):
353 # In Python, audiotest.au lives in Lib/test not Lib/test/data
354 fp = open(findfile('audiotest.au'))
355 try:
356 self._audiodata = fp.read()
357 finally:
358 fp.close()
359 self._au = MIMEAudio(self._audiodata)
360
361 def test_guess_minor_type(self):
362 self.assertEqual(self._au.get_type(), 'audio/basic')
363
364 def test_encoding(self):
365 payload = self._au.get_payload()
366 self.assertEqual(base64.decodestring(payload), self._audiodata)
367
368 def checkSetMinor(self):
369 au = MIMEAudio(self._audiodata, 'fish')
370 self.assertEqual(im.get_type(), 'audio/fish')
371
372 def test_custom_encoder(self):
373 eq = self.assertEqual
374 def encoder(msg):
375 orig = msg.get_payload()
376 msg.set_payload(0)
377 msg['Content-Transfer-Encoding'] = 'broken64'
378 au = MIMEAudio(self._audiodata, _encoder=encoder)
379 eq(au.get_payload(), 0)
380 eq(au['content-transfer-encoding'], 'broken64')
381
382 def test_add_header(self):
383 eq = self.assertEqual
384 unless = self.failUnless
385 self._au.add_header('Content-Disposition', 'attachment',
386 filename='audiotest.au')
387 eq(self._au['content-disposition'],
388 'attachment; filename="audiotest.au"')
389 eq(self._au.get_params(header='content-disposition'),
390 [('attachment', ''), ('filename', 'audiotest.au')])
391 eq(self._au.get_param('filename', header='content-disposition'),
392 'audiotest.au')
393 missing = []
394 eq(self._au.get_param('attachment', header='content-disposition'), '')
395 unless(self._au.get_param('foo', failobj=missing,
396 header='content-disposition') is missing)
397 # Try some missing stuff
398 unless(self._au.get_param('foobar', missing) is missing)
399 unless(self._au.get_param('attachment', missing,
400 header='foobar') is missing)
401
402
403
Barry Warsaw65279d02001-09-26 05:47:08 +0000404# Test the basic MIMEImage class
405class TestMIMEImage(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000406 def setUp(self):
407 fp = openfile('PyBanner048.gif')
408 try:
409 self._imgdata = fp.read()
410 finally:
411 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000412 self._im = MIMEImage(self._imgdata)
Barry Warsaw41075852001-09-23 03:18:13 +0000413
414 def test_guess_minor_type(self):
415 self.assertEqual(self._im.get_type(), 'image/gif')
416
417 def test_encoding(self):
418 payload = self._im.get_payload()
419 self.assertEqual(base64.decodestring(payload), self._imgdata)
420
421 def checkSetMinor(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000422 im = MIMEImage(self._imgdata, 'fish')
Barry Warsaw41075852001-09-23 03:18:13 +0000423 self.assertEqual(im.get_type(), 'image/fish')
424
425 def test_custom_encoder(self):
426 eq = self.assertEqual
427 def encoder(msg):
428 orig = msg.get_payload()
429 msg.set_payload(0)
430 msg['Content-Transfer-Encoding'] = 'broken64'
Barry Warsaw65279d02001-09-26 05:47:08 +0000431 im = MIMEImage(self._imgdata, _encoder=encoder)
Barry Warsaw41075852001-09-23 03:18:13 +0000432 eq(im.get_payload(), 0)
433 eq(im['content-transfer-encoding'], 'broken64')
434
435 def test_add_header(self):
436 eq = self.assertEqual
437 unless = self.failUnless
438 self._im.add_header('Content-Disposition', 'attachment',
439 filename='dingusfish.gif')
440 eq(self._im['content-disposition'],
441 'attachment; filename="dingusfish.gif"')
442 eq(self._im.get_params(header='content-disposition'),
Barry Warsaw65279d02001-09-26 05:47:08 +0000443 [('attachment', ''), ('filename', 'dingusfish.gif')])
Barry Warsaw41075852001-09-23 03:18:13 +0000444 eq(self._im.get_param('filename', header='content-disposition'),
445 'dingusfish.gif')
446 missing = []
Barry Warsaw65279d02001-09-26 05:47:08 +0000447 eq(self._im.get_param('attachment', header='content-disposition'), '')
448 unless(self._im.get_param('foo', failobj=missing,
Barry Warsaw41075852001-09-23 03:18:13 +0000449 header='content-disposition') is missing)
450 # Try some missing stuff
451 unless(self._im.get_param('foobar', missing) is missing)
452 unless(self._im.get_param('attachment', missing,
453 header='foobar') is missing)
454
455
Barry Warsaw08a534d2001-10-04 17:58:50 +0000456
Barry Warsaw65279d02001-09-26 05:47:08 +0000457# Test the basic MIMEText class
458class TestMIMEText(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000459 def setUp(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000460 self._msg = MIMEText('hello there')
Barry Warsaw41075852001-09-23 03:18:13 +0000461
462 def test_types(self):
463 eq = self.assertEqual
464 unless = self.failUnless
465 eq(self._msg.get_type(), 'text/plain')
466 eq(self._msg.get_param('charset'), 'us-ascii')
467 missing = []
468 unless(self._msg.get_param('foobar', missing) is missing)
469 unless(self._msg.get_param('charset', missing, header='foobar')
470 is missing)
471
472 def test_payload(self):
473 self.assertEqual(self._msg.get_payload(), 'hello there\n')
474 self.failUnless(not self._msg.is_multipart())
475
476
Barry Warsaw08a534d2001-10-04 17:58:50 +0000477
478# Test a more complicated multipart/mixed type message
Barry Warsaw41075852001-09-23 03:18:13 +0000479class TestMultipartMixed(unittest.TestCase):
480 def setUp(self):
481 fp = openfile('PyBanner048.gif')
482 try:
483 data = fp.read()
484 finally:
485 fp.close()
486
487 container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
Barry Warsaw65279d02001-09-26 05:47:08 +0000488 image = MIMEImage(data, name='dingusfish.gif')
Barry Warsaw41075852001-09-23 03:18:13 +0000489 image.add_header('content-disposition', 'attachment',
490 filename='dingusfish.gif')
Barry Warsaw65279d02001-09-26 05:47:08 +0000491 intro = MIMEText('''\
Barry Warsaw41075852001-09-23 03:18:13 +0000492Hi there,
493
494This is the dingus fish.
495''')
496 container.add_payload(intro)
497 container.add_payload(image)
498 container['From'] = 'Barry <barry@digicool.com>'
499 container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
500 container['Subject'] = 'Here is your dingus fish'
Tim Peters527e64f2001-10-04 05:36:56 +0000501
Barry Warsaw41075852001-09-23 03:18:13 +0000502 now = 987809702.54848599
503 timetuple = time.localtime(now)
504 if timetuple[-1] == 0:
505 tzsecs = time.timezone
506 else:
507 tzsecs = time.altzone
508 if tzsecs > 0:
509 sign = '-'
510 else:
511 sign = '+'
512 tzoffset = ' %s%04d' % (sign, tzsecs / 36)
513 container['Date'] = time.strftime(
514 '%a, %d %b %Y %H:%M:%S',
515 time.localtime(now)) + tzoffset
516 self._msg = container
517 self._im = image
518 self._txt = intro
519
520 def test_hierarchy(self):
521 # convenience
522 eq = self.assertEqual
523 unless = self.failUnless
524 raises = self.assertRaises
525 # tests
526 m = self._msg
527 unless(m.is_multipart())
528 eq(m.get_type(), 'multipart/mixed')
529 eq(len(m.get_payload()), 2)
530 raises(IndexError, m.get_payload, 2)
531 m0 = m.get_payload(0)
532 m1 = m.get_payload(1)
533 unless(m0 is self._txt)
534 unless(m1 is self._im)
535 eq(m.get_payload(), [m0, m1])
536 unless(not m0.is_multipart())
537 unless(not m1.is_multipart())
538
539
Barry Warsaw08a534d2001-10-04 17:58:50 +0000540
541# Test some badly formatted messages
Barry Warsaw41075852001-09-23 03:18:13 +0000542class TestNonConformant(TestEmailBase):
543 def test_parse_missing_minor_type(self):
544 eq = self.assertEqual
545 msg = self._msgobj('msg_14.txt')
546 eq(msg.get_type(), 'text')
547 eq(msg.get_main_type(), 'text')
548 self.failUnless(msg.get_subtype() is None)
549
550 def test_bogus_boundary(self):
551 fp = openfile('msg_15.txt')
552 try:
553 data = fp.read()
554 finally:
555 fp.close()
556 p = Parser()
557 # Note, under a future non-strict parsing mode, this would parse the
558 # message into the intended message tree.
559 self.assertRaises(Errors.BoundaryError, p.parsestr, data)
560
561
Barry Warsaw08a534d2001-10-04 17:58:50 +0000562
563# Test RFC 2047 header encoding and decoding
Barry Warsaw41075852001-09-23 03:18:13 +0000564class TestRFC2047(unittest.TestCase):
565 def test_iso_8859_1(self):
566 eq = self.assertEqual
567 s = '=?iso-8859-1?q?this=20is=20some=20text?='
568 eq(Utils.decode(s), 'this is some text')
569 s = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?='
570 eq(Utils.decode(s), u'Keld_J\xf8rn_Simonsen')
571 s = '=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=' \
572 '=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
573 eq(Utils.decode(s), 'If you can read this you understand the example.')
574 s = '=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?='
575 eq(Utils.decode(s),
576 u'\u05dd\u05d5\u05dc\u05e9 \u05df\u05d1 \u05d9\u05dc\u05d8\u05e4\u05e0')
577 s = '=?iso-8859-1?q?this=20is?= =?iso-8859-1?q?some=20text?='
578 eq(Utils.decode(s), u'this is some text')
579
580 def test_encode_header(self):
581 eq = self.assertEqual
582 s = 'this is some text'
583 eq(Utils.encode(s), '=?iso-8859-1?q?this=20is=20some=20text?=')
584 s = 'Keld_J\xf8rn_Simonsen'
585 eq(Utils.encode(s), '=?iso-8859-1?q?Keld_J=F8rn_Simonsen?=')
586 s1 = 'If you can read this yo'
587 s2 = 'u understand the example.'
588 eq(Utils.encode(s1, encoding='b'),
589 '=?iso-8859-1?b?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=')
590 eq(Utils.encode(s2, charset='iso-8859-2', encoding='b'),
591 '=?iso-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=')
592
593
Barry Warsaw08a534d2001-10-04 17:58:50 +0000594
595# Test the MIMEMessage class
Barry Warsaw65279d02001-09-26 05:47:08 +0000596class TestMIMEMessage(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +0000597 def setUp(self):
598 fp = openfile('msg_11.txt')
599 self._text = fp.read()
600 fp.close()
601
602 def test_type_error(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000603 self.assertRaises(TypeError, MIMEMessage, 'a plain string')
Barry Warsaw41075852001-09-23 03:18:13 +0000604
605 def test_valid_argument(self):
606 eq = self.assertEqual
607 subject = 'A sub-message'
608 m = Message()
609 m['Subject'] = subject
Barry Warsaw65279d02001-09-26 05:47:08 +0000610 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000611 eq(r.get_type(), 'message/rfc822')
612 self.failUnless(r.get_payload() is m)
613 eq(r.get_payload()['subject'], subject)
614
615 def test_generate(self):
616 # First craft the message to be encapsulated
617 m = Message()
618 m['Subject'] = 'An enclosed message'
619 m.add_payload('Here is the body of the message.\n')
Barry Warsaw65279d02001-09-26 05:47:08 +0000620 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000621 r['Subject'] = 'The enclosing message'
622 s = StringIO()
623 g = Generator(s)
624 g(r)
Barry Warsaw65279d02001-09-26 05:47:08 +0000625 self.assertEqual(s.getvalue(), """\
626Content-Type: message/rfc822
627MIME-Version: 1.0
628Subject: The enclosing message
629
630Subject: An enclosed message
631
632Here is the body of the message.
633""")
634
635 def test_parse_message_rfc822(self):
636 eq = self.assertEqual
637 msg = self._msgobj('msg_11.txt')
638 eq(msg.get_type(), 'message/rfc822')
639 eq(len(msg.get_payload()), 1)
640 submsg = msg.get_payload()
641 self.failUnless(isinstance(submsg, Message))
642 eq(submsg['subject'], 'An enclosed message')
643 eq(submsg.get_payload(), 'Here is the body of the message.\n')
644
645 def test_dsn(self):
646 eq = self.assertEqual
647 unless = self.failUnless
648 # msg 16 is a Delivery Status Notification, see RFC XXXX
649 msg = self._msgobj('msg_16.txt')
650 eq(msg.get_type(), 'multipart/report')
651 unless(msg.is_multipart())
652 eq(len(msg.get_payload()), 3)
653 # Subpart 1 is a text/plain, human readable section
654 subpart = msg.get_payload(0)
655 eq(subpart.get_type(), 'text/plain')
656 eq(subpart.get_payload(), """\
657This report relates to a message you sent with the following header fields:
658
659 Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
660 Date: Sun, 23 Sep 2001 20:10:55 -0700
661 From: "Ian T. Henry" <henryi@oxy.edu>
662 To: SoCal Raves <scr@socal-raves.org>
663 Subject: [scr] yeah for Ians!!
664
665Your message cannot be delivered to the following recipients:
666
667 Recipient address: jangel1@cougar.noc.ucla.edu
668 Reason: recipient reached disk quota
669
670""")
671 # Subpart 2 contains the machine parsable DSN information. It
672 # consists of two blocks of headers, represented by two nested Message
673 # objects.
674 subpart = msg.get_payload(1)
675 eq(subpart.get_type(), 'message/delivery-status')
676 eq(len(subpart.get_payload()), 2)
677 # message/delivery-status should treat each block as a bunch of
678 # headers, i.e. a bunch of Message objects.
679 dsn1 = subpart.get_payload(0)
680 unless(isinstance(dsn1, Message))
681 eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu')
682 eq(dsn1.get_param('dns', header='reporting-mta'), '')
683 # Try a missing one <wink>
684 eq(dsn1.get_param('nsd', header='reporting-mta'), None)
685 dsn2 = subpart.get_payload(1)
686 unless(isinstance(dsn2, Message))
687 eq(dsn2['action'], 'failed')
688 eq(dsn2.get_params(header='original-recipient'),
689 [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')])
690 eq(dsn2.get_param('rfc822', header='final-recipient'), '')
691 # Subpart 3 is the original message
692 subpart = msg.get_payload(2)
693 eq(subpart.get_type(), 'message/rfc822')
694 subsubpart = subpart.get_payload()
695 unless(isinstance(subsubpart, Message))
696 eq(subsubpart.get_type(), 'text/plain')
697 eq(subsubpart['message-id'],
698 '<002001c144a6$8752e060$56104586@oxy.edu>')
Barry Warsaw41075852001-09-23 03:18:13 +0000699
Barry Warsaw1f0fa922001-10-19 04:08:59 +0000700 def test_epilogue(self):
701 fp = openfile('msg_21.txt')
702 try:
703 text = fp.read()
704 finally:
705 fp.close()
706 msg = Message()
707 msg['From'] = 'aperson@dom.ain'
708 msg['To'] = 'bperson@dom.ain'
709 msg['Subject'] = 'Test'
710 msg.preamble = 'MIME message\n'
711 msg.epilogue = 'End of MIME message\n'
712 msg1 = MIMEText('One')
713 msg2 = MIMEText('Two')
714 msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
715 msg.add_payload(msg1)
716 msg.add_payload(msg2)
717 sfp = StringIO()
718 g = Generator(sfp)
719 g(msg)
720 self.assertEqual(sfp.getvalue(), text)
721
Barry Warsaw41075852001-09-23 03:18:13 +0000722
Barry Warsaw08a534d2001-10-04 17:58:50 +0000723
724# A general test of parser->model->generator idempotency. IOW, read a message
725# in, parse it into a message object tree, then without touching the tree,
726# regenerate the plain text. The original text and the transformed text
727# should be identical. Note: that we ignore the Unix-From since that may
728# contain a changed date.
Barry Warsaw41075852001-09-23 03:18:13 +0000729class TestIdempotent(unittest.TestCase):
730 def _msgobj(self, filename):
731 fp = openfile(filename)
732 try:
733 data = fp.read()
734 finally:
735 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000736 msg = email.message_from_string(data)
737 return msg, data
Barry Warsaw41075852001-09-23 03:18:13 +0000738
739 def _idempotent(self, msg, text):
740 eq = self.assertEquals
741 s = StringIO()
742 g = Generator(s, maxheaderlen=0)
743 g(msg)
744 eq(text, s.getvalue())
745
746 def test_parse_text_message(self):
747 eq = self.assertEquals
748 msg, text = self._msgobj('msg_01.txt')
749 eq(msg.get_type(), 'text/plain')
750 eq(msg.get_main_type(), 'text')
751 eq(msg.get_subtype(), 'plain')
Barry Warsaw65279d02001-09-26 05:47:08 +0000752 eq(msg.get_params()[1], ('charset', 'us-ascii'))
Barry Warsaw41075852001-09-23 03:18:13 +0000753 eq(msg.get_param('charset'), 'us-ascii')
754 eq(msg.preamble, None)
755 eq(msg.epilogue, None)
756 self._idempotent(msg, text)
757
758 def test_parse_untyped_message(self):
759 eq = self.assertEquals
760 msg, text = self._msgobj('msg_03.txt')
761 eq(msg.get_type(), None)
762 eq(msg.get_params(), None)
763 eq(msg.get_param('charset'), None)
764 self._idempotent(msg, text)
765
766 def test_simple_multipart(self):
767 msg, text = self._msgobj('msg_04.txt')
768 self._idempotent(msg, text)
769
770 def test_MIME_digest(self):
771 msg, text = self._msgobj('msg_02.txt')
772 self._idempotent(msg, text)
773
774 def test_mixed_with_image(self):
775 msg, text = self._msgobj('msg_06.txt')
776 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +0000777
Barry Warsaw41075852001-09-23 03:18:13 +0000778 def test_multipart_report(self):
779 msg, text = self._msgobj('msg_05.txt')
780 self._idempotent(msg, text)
Barry Warsaw65279d02001-09-26 05:47:08 +0000781
782 def test_dsn(self):
783 msg, text = self._msgobj('msg_16.txt')
784 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +0000785
Barry Warsaw1f0fa922001-10-19 04:08:59 +0000786 def test_preamble_epilogue(self):
787 msg, text = self._msgobj('msg_21.txt')
788 self._idempotent(msg, text)
789
Barry Warsaw41075852001-09-23 03:18:13 +0000790 def test_content_type(self):
791 eq = self.assertEquals
792 # Get a message object and reset the seek pointer for other tests
793 msg, text = self._msgobj('msg_05.txt')
794 eq(msg.get_type(), 'multipart/report')
795 # Test the Content-Type: parameters
796 params = {}
Barry Warsaw65279d02001-09-26 05:47:08 +0000797 for pk, pv in msg.get_params():
Barry Warsaw41075852001-09-23 03:18:13 +0000798 params[pk] = pv
799 eq(params['report-type'], 'delivery-status')
Barry Warsaw65279d02001-09-26 05:47:08 +0000800 eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
Barry Warsaw41075852001-09-23 03:18:13 +0000801 eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
802 eq(msg.epilogue, '\n\n')
803 eq(len(msg.get_payload()), 3)
804 # Make sure the subparts are what we expect
805 msg1 = msg.get_payload(0)
806 eq(msg1.get_type(), 'text/plain')
807 eq(msg1.get_payload(), 'Yadda yadda yadda\n')
808 msg2 = msg.get_payload(1)
809 eq(msg2.get_type(), None)
810 eq(msg2.get_payload(), 'Yadda yadda yadda\n')
811 msg3 = msg.get_payload(2)
812 eq(msg3.get_type(), 'message/rfc822')
813 self.failUnless(isinstance(msg3, Message))
814 msg4 = msg3.get_payload()
815 self.failUnless(isinstance(msg4, Message))
816 eq(msg4.get_payload(), 'Yadda yadda yadda\n')
817
818 def test_parser(self):
819 eq = self.assertEquals
820 msg, text = self._msgobj('msg_06.txt')
821 # Check some of the outer headers
822 eq(msg.get_type(), 'message/rfc822')
823 # Make sure there's exactly one thing in the payload and that's a
824 # sub-Message object of type text/plain
825 msg1 = msg.get_payload()
826 self.failUnless(isinstance(msg1, Message))
827 eq(msg1.get_type(), 'text/plain')
828 self.failUnless(isinstance(msg1.get_payload(), StringType))
829 eq(msg1.get_payload(), '\n')
Barry Warsaw41075852001-09-23 03:18:13 +0000830
Tim Peters527e64f2001-10-04 05:36:56 +0000831
Barry Warsaw08a534d2001-10-04 17:58:50 +0000832
833# Test various other bits of the package's functionality
Barry Warsaw41075852001-09-23 03:18:13 +0000834class TestMiscellaneous(unittest.TestCase):
835 def test_message_from_string(self):
836 fp = openfile('msg_01.txt')
837 try:
838 text = fp.read()
839 finally:
840 fp.close()
841 msg = email.message_from_string(text)
842 s = StringIO()
843 # Don't wrap/continue long headers since we're trying to test
844 # idempotency.
845 g = Generator(s, maxheaderlen=0)
846 g(msg)
847 self.assertEqual(text, s.getvalue())
848
849 def test_message_from_file(self):
850 fp = openfile('msg_01.txt')
851 try:
852 text = fp.read()
853 fp.seek(0)
854 msg = email.message_from_file(fp)
855 s = StringIO()
856 # Don't wrap/continue long headers since we're trying to test
857 # idempotency.
858 g = Generator(s, maxheaderlen=0)
859 g(msg)
860 self.assertEqual(text, s.getvalue())
861 finally:
862 fp.close()
863
864 def test_message_from_string_with_class(self):
865 unless = self.failUnless
866 fp = openfile('msg_01.txt')
867 try:
868 text = fp.read()
869 finally:
870 fp.close()
871 # Create a subclass
872 class MyMessage(Message):
873 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000874
Barry Warsaw41075852001-09-23 03:18:13 +0000875 msg = email.message_from_string(text, MyMessage)
876 unless(isinstance(msg, MyMessage))
877 # Try something more complicated
878 fp = openfile('msg_02.txt')
879 try:
880 text = fp.read()
881 finally:
882 fp.close()
883 msg = email.message_from_string(text, MyMessage)
884 for subpart in msg.walk():
885 unless(isinstance(subpart, MyMessage))
886
Barry Warsaw41075852001-09-23 03:18:13 +0000887 def test_message_from_file_with_class(self):
888 unless = self.failUnless
889 # Create a subclass
890 class MyMessage(Message):
891 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000892
Barry Warsaw41075852001-09-23 03:18:13 +0000893 fp = openfile('msg_01.txt')
894 try:
895 msg = email.message_from_file(fp, MyMessage)
896 finally:
897 fp.close()
898 unless(isinstance(msg, MyMessage))
899 # Try something more complicated
900 fp = openfile('msg_02.txt')
901 try:
902 msg = email.message_from_file(fp, MyMessage)
903 finally:
904 fp.close()
905 for subpart in msg.walk():
906 unless(isinstance(subpart, MyMessage))
907
Barry Warsawfee435a2001-10-09 19:23:57 +0000908 def test__all__(self):
909 module = __import__('email')
910 all = module.__all__
911 all.sort()
912 self.assertEqual(all, ['Encoders', 'Errors', 'Generator', 'Iterators',
913 'MIMEAudio', 'MIMEBase', 'MIMEImage',
914 'MIMEMessage', 'MIMEText', 'Message', 'Parser',
915 'Utils',
916 'message_from_file', 'message_from_string'])
917
Barry Warsaw41075852001-09-23 03:18:13 +0000918
Barry Warsaw08a534d2001-10-04 17:58:50 +0000919
920# Test the iterator/generators
Barry Warsaw41075852001-09-23 03:18:13 +0000921class TestIterators(TestEmailBase):
922 def test_body_line_iterator(self):
923 eq = self.assertEqual
924 # First a simple non-multipart message
925 msg = self._msgobj('msg_01.txt')
926 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000927 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000928 eq(len(lines), 6)
929 eq(EMPTYSTRING.join(lines), msg.get_payload())
930 # Now a more complicated multipart
931 msg = self._msgobj('msg_02.txt')
932 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000933 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000934 eq(len(lines), 43)
Barry Warsaw08a534d2001-10-04 17:58:50 +0000935 eq(EMPTYSTRING.join(lines), openfile('msg_19.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +0000936
937 def test_typed_subpart_iterator(self):
938 eq = self.assertEqual
939 msg = self._msgobj('msg_04.txt')
940 it = Iterators.typed_subpart_iterator(msg, 'text')
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000941 lines = [subpart.get_payload() for subpart in it]
942 eq(len(lines), 2)
Barry Warsaw41075852001-09-23 03:18:13 +0000943 eq(EMPTYSTRING.join(lines), """\
944a simple kind of mirror
945to reflect upon our own
946a simple kind of mirror
947to reflect upon our own
948""")
949
Barry Warsawcdc632c2001-10-15 04:39:02 +0000950 def test_typed_subpart_iterator_default_type(self):
951 eq = self.assertEqual
952 msg = self._msgobj('msg_03.txt')
953 it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
954 lines = []
955 subparts = 0
956 for subpart in it:
957 subparts += 1
958 lines.append(subpart.get_payload())
959 eq(subparts, 1)
960 eq(EMPTYSTRING.join(lines), """\
961
962Hi,
963
964Do you like this message?
965
966-Me
967""")
Barry Warsaw41075852001-09-23 03:18:13 +0000968
Barry Warsaw08a534d2001-10-04 17:58:50 +0000969
Barry Warsawbf7a59d2001-10-11 15:44:50 +0000970class TestParsers(unittest.TestCase):
971 def test_header_parser(self):
972 eq = self.assertEqual
973 # Parse only the headers of a complex multipart MIME document
974 p = HeaderParser()
975 fp = openfile('msg_02.txt')
976 msg = p.parse(fp)
977 eq(msg['from'], 'ppp-request@zzz.org')
978 eq(msg['to'], 'ppp@zzz.org')
979 eq(msg.get_type(), 'multipart/mixed')
980 eq(msg.is_multipart(), 0)
981 self.failUnless(isinstance(msg.get_payload(), StringType))
982
983
984
Barry Warsaw41075852001-09-23 03:18:13 +0000985def suite():
986 suite = unittest.TestSuite()
987 suite.addTest(unittest.makeSuite(TestMessageAPI))
988 suite.addTest(unittest.makeSuite(TestEncoders))
989 suite.addTest(unittest.makeSuite(TestLongHeaders))
990 suite.addTest(unittest.makeSuite(TestFromMangling))
Barry Warsawfee435a2001-10-09 19:23:57 +0000991 suite.addTest(unittest.makeSuite(TestMIMEAudio))
Barry Warsaw65279d02001-09-26 05:47:08 +0000992 suite.addTest(unittest.makeSuite(TestMIMEImage))
993 suite.addTest(unittest.makeSuite(TestMIMEText))
Barry Warsaw41075852001-09-23 03:18:13 +0000994 suite.addTest(unittest.makeSuite(TestMultipartMixed))
995 suite.addTest(unittest.makeSuite(TestNonConformant))
996 suite.addTest(unittest.makeSuite(TestRFC2047))
Barry Warsaw65279d02001-09-26 05:47:08 +0000997 suite.addTest(unittest.makeSuite(TestMIMEMessage))
Barry Warsaw41075852001-09-23 03:18:13 +0000998 suite.addTest(unittest.makeSuite(TestIdempotent))
999 suite.addTest(unittest.makeSuite(TestMiscellaneous))
1000 suite.addTest(unittest.makeSuite(TestIterators))
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001001 suite.addTest(unittest.makeSuite(TestParsers))
Barry Warsaw41075852001-09-23 03:18:13 +00001002 return suite
1003
1004
Barry Warsaw08a534d2001-10-04 17:58:50 +00001005
Barry Warsaw41075852001-09-23 03:18:13 +00001006if __name__ == '__main__':
1007 unittest.main(defaultTest='suite')
1008else:
1009 from test_support import run_suite
1010 run_suite(suite())