blob: 5be7e95d9a9d044129942a23a2ff320f6006d049 [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
Guido van Rossum78f0dd32001-12-07 21:07:08 +000026from test_support import findfile, __file__ as test_support_file
Barry Warsaw41075852001-09-23 03:18:13 +000027
Barry Warsaw1f0fa922001-10-19 04:08:59 +000028
Barry Warsaw41075852001-09-23 03:18:13 +000029NL = '\n'
30EMPTYSTRING = ''
Barry Warsaw07227d12001-10-17 20:52:26 +000031SPACE = ' '
Barry Warsaw41075852001-09-23 03:18:13 +000032
33
Barry Warsaw08a534d2001-10-04 17:58:50 +000034
Barry Warsaw41075852001-09-23 03:18:13 +000035def openfile(filename):
Guido van Rossum78f0dd32001-12-07 21:07:08 +000036 path = os.path.join(os.path.dirname(test_support_file), 'data', filename)
Barry Warsaw41075852001-09-23 03:18:13 +000037 return open(path)
38
39
Barry Warsaw08a534d2001-10-04 17:58:50 +000040
Barry Warsaw41075852001-09-23 03:18:13 +000041# Base test class
42class TestEmailBase(unittest.TestCase):
43 def _msgobj(self, filename):
44 fp = openfile(filename)
45 try:
Barry Warsaw65279d02001-09-26 05:47:08 +000046 msg = email.message_from_file(fp)
Barry Warsaw41075852001-09-23 03:18:13 +000047 finally:
48 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +000049 return msg
Barry Warsaw41075852001-09-23 03:18:13 +000050
51
Barry Warsaw08a534d2001-10-04 17:58:50 +000052
Barry Warsaw41075852001-09-23 03:18:13 +000053# Test various aspects of the Message class's API
54class TestMessageAPI(TestEmailBase):
Barry Warsaw2f6a0b02001-10-09 15:49:35 +000055 def test_get_all(self):
56 eq = self.assertEqual
57 msg = self._msgobj('msg_20.txt')
58 eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
59 eq(msg.get_all('xx', 'n/a'), 'n/a')
60
Barry Warsaw41075852001-09-23 03:18:13 +000061 def test_get_charsets(self):
62 eq = self.assertEqual
Tim Peters527e64f2001-10-04 05:36:56 +000063
Barry Warsaw65279d02001-09-26 05:47:08 +000064 msg = self._msgobj('msg_08.txt')
65 charsets = msg.get_charsets()
66 eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000067
Barry Warsaw65279d02001-09-26 05:47:08 +000068 msg = self._msgobj('msg_09.txt')
69 charsets = msg.get_charsets('dingbat')
70 eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
71 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000072
Barry Warsaw65279d02001-09-26 05:47:08 +000073 msg = self._msgobj('msg_12.txt')
74 charsets = msg.get_charsets()
75 eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
76 'iso-8859-3', 'us-ascii', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +000077
78 def test_get_filename(self):
79 eq = self.assertEqual
80
Barry Warsaw65279d02001-09-26 05:47:08 +000081 msg = self._msgobj('msg_04.txt')
82 filenames = [p.get_filename() for p in msg.get_payload()]
Barry Warsaw41075852001-09-23 03:18:13 +000083 eq(filenames, ['msg.txt', 'msg.txt'])
84
Barry Warsaw65279d02001-09-26 05:47:08 +000085 msg = self._msgobj('msg_07.txt')
86 subpart = msg.get_payload(1)
Barry Warsaw41075852001-09-23 03:18:13 +000087 eq(subpart.get_filename(), 'dingusfish.gif')
88
89 def test_get_boundary(self):
90 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +000091 msg = self._msgobj('msg_07.txt')
Barry Warsaw41075852001-09-23 03:18:13 +000092 # No quotes!
Barry Warsaw65279d02001-09-26 05:47:08 +000093 eq(msg.get_boundary(), 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +000094
95 def test_set_boundary(self):
96 eq = self.assertEqual
97 # This one has no existing boundary parameter, but the Content-Type:
98 # header appears fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +000099 msg = self._msgobj('msg_01.txt')
100 msg.set_boundary('BOUNDARY')
101 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000102 eq(header.lower(), 'content-type')
103 eq(value, 'text/plain; charset=us-ascii; boundary="BOUNDARY"')
104 # This one has a Content-Type: header, with a boundary, stuck in the
105 # middle of its headers. Make sure the order is preserved; it should
106 # be fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000107 msg = self._msgobj('msg_04.txt')
108 msg.set_boundary('BOUNDARY')
109 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000110 eq(header.lower(), 'content-type')
111 eq(value, 'multipart/mixed; boundary="BOUNDARY"')
112 # And this one has no Content-Type: header at all.
Barry Warsaw65279d02001-09-26 05:47:08 +0000113 msg = self._msgobj('msg_03.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000114 self.assertRaises(Errors.HeaderParseError,
Barry Warsaw65279d02001-09-26 05:47:08 +0000115 msg.set_boundary, 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000116
117 def test_get_decoded_payload(self):
118 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000119 msg = self._msgobj('msg_10.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000120 # The outer message is a multipart
Barry Warsaw65279d02001-09-26 05:47:08 +0000121 eq(msg.get_payload(decode=1), None)
Barry Warsaw41075852001-09-23 03:18:13 +0000122 # Subpart 1 is 7bit encoded
Barry Warsaw65279d02001-09-26 05:47:08 +0000123 eq(msg.get_payload(0).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000124 'This is a 7bit encoded message.\n')
125 # Subpart 2 is quopri
Barry Warsaw65279d02001-09-26 05:47:08 +0000126 eq(msg.get_payload(1).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000127 '\xa1This is a Quoted Printable encoded message!\n')
128 # Subpart 3 is base64
Barry Warsaw65279d02001-09-26 05:47:08 +0000129 eq(msg.get_payload(2).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000130 'This is a Base64 encoded message.')
131 # Subpart 4 has no Content-Transfer-Encoding: header.
Barry Warsaw65279d02001-09-26 05:47:08 +0000132 eq(msg.get_payload(3).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000133 'This has no Content-Transfer-Encoding: header.\n')
134
135 def test_decoded_generator(self):
136 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000137 msg = self._msgobj('msg_07.txt')
138 fp = openfile('msg_17.txt')
139 try:
140 text = fp.read()
141 finally:
142 fp.close()
Barry Warsaw41075852001-09-23 03:18:13 +0000143 s = StringIO()
144 g = DecodedGenerator(s)
Barry Warsaw65279d02001-09-26 05:47:08 +0000145 g(msg)
146 eq(s.getvalue(), text)
Barry Warsaw41075852001-09-23 03:18:13 +0000147
148 def test__contains__(self):
149 msg = Message()
150 msg['From'] = 'Me'
151 msg['to'] = 'You'
152 # Check for case insensitivity
153 self.failUnless('from' in msg)
154 self.failUnless('From' in msg)
155 self.failUnless('FROM' in msg)
156 self.failUnless('to' in msg)
157 self.failUnless('To' in msg)
158 self.failUnless('TO' in msg)
159
160 def test_as_string(self):
161 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000162 msg = self._msgobj('msg_01.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000163 fp = openfile('msg_01.txt')
164 try:
165 text = fp.read()
166 finally:
167 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000168 eq(text, msg.as_string())
169 fullrepr = str(msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000170 lines = fullrepr.split('\n')
171 self.failUnless(lines[0].startswith('From '))
172 eq(text, NL.join(lines[1:]))
173
174 def test_bad_param(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000175 msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000176 self.assertEqual(msg.get_param('baz'), '')
177
178 def test_missing_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000179 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000180 self.assertEqual(msg.get_filename(), None)
181
182 def test_bogus_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000183 msg = email.message_from_string(
184 "Content-Disposition: blarg; filename\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000185 self.assertEqual(msg.get_filename(), '')
Tim Peters527e64f2001-10-04 05:36:56 +0000186
Barry Warsaw41075852001-09-23 03:18:13 +0000187 def test_missing_boundary(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000188 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000189 self.assertEqual(msg.get_boundary(), None)
190
Barry Warsaw65279d02001-09-26 05:47:08 +0000191 def test_get_params(self):
192 eq = self.assertEqual
193 msg = email.message_from_string(
194 'X-Header: foo=one; bar=two; baz=three\n')
195 eq(msg.get_params(header='x-header'),
196 [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
197 msg = email.message_from_string(
198 'X-Header: foo; bar=one; baz=two\n')
199 eq(msg.get_params(header='x-header'),
200 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
201 eq(msg.get_params(), None)
202 msg = email.message_from_string(
203 'X-Header: foo; bar="one"; baz=two\n')
204 eq(msg.get_params(header='x-header'),
205 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
206
207 def test_get_param(self):
208 eq = self.assertEqual
209 msg = email.message_from_string(
210 "X-Header: foo=one; bar=two; baz=three\n")
211 eq(msg.get_param('bar', header='x-header'), 'two')
212 eq(msg.get_param('quuz', header='x-header'), None)
213 eq(msg.get_param('quuz'), None)
214 msg = email.message_from_string(
215 'X-Header: foo; bar="one"; baz=two\n')
216 eq(msg.get_param('foo', header='x-header'), '')
217 eq(msg.get_param('bar', header='x-header'), 'one')
218 eq(msg.get_param('baz', header='x-header'), 'two')
219
Barry Warsaw2539cf52001-10-25 22:43:46 +0000220 def test_get_param_funky_continuation_lines(self):
221 msg = self._msgobj('msg_22.txt')
222 self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG')
223
Barry Warsaw65279d02001-09-26 05:47:08 +0000224 def test_has_key(self):
225 msg = email.message_from_string('Header: exists')
226 self.failUnless(msg.has_key('header'))
227 self.failUnless(msg.has_key('Header'))
228 self.failUnless(msg.has_key('HEADER'))
229 self.failIf(msg.has_key('headeri'))
230
Barry Warsaw41075852001-09-23 03:18:13 +0000231
Barry Warsaw08a534d2001-10-04 17:58:50 +0000232
Barry Warsaw41075852001-09-23 03:18:13 +0000233# Test the email.Encoders module
234class TestEncoders(unittest.TestCase):
235 def test_encode_noop(self):
236 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000237 msg = MIMEText('hello world', _encoder=Encoders.encode_noop)
Barry Warsaw41075852001-09-23 03:18:13 +0000238 eq(msg.get_payload(), 'hello world\n')
239 eq(msg['content-transfer-encoding'], None)
240
241 def test_encode_7bit(self):
242 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000243 msg = MIMEText('hello world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000244 eq(msg.get_payload(), 'hello world\n')
245 eq(msg['content-transfer-encoding'], '7bit')
Barry Warsaw65279d02001-09-26 05:47:08 +0000246 msg = MIMEText('hello \x7f world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000247 eq(msg.get_payload(), 'hello \x7f world\n')
248 eq(msg['content-transfer-encoding'], '7bit')
249
250 def test_encode_8bit(self):
251 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000252 msg = MIMEText('hello \x80 world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000253 eq(msg.get_payload(), 'hello \x80 world\n')
254 eq(msg['content-transfer-encoding'], '8bit')
255
256 def test_encode_base64(self):
257 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000258 msg = MIMEText('hello world', _encoder=Encoders.encode_base64)
Barry Warsaw41075852001-09-23 03:18:13 +0000259 eq(msg.get_payload(), 'aGVsbG8gd29ybGQK\n')
260 eq(msg['content-transfer-encoding'], 'base64')
261
262 def test_encode_quoted_printable(self):
263 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000264 msg = MIMEText('hello world', _encoder=Encoders.encode_quopri)
Barry Warsaw41075852001-09-23 03:18:13 +0000265 eq(msg.get_payload(), 'hello=20world\n')
266 eq(msg['content-transfer-encoding'], 'quoted-printable')
267
268
Barry Warsaw08a534d2001-10-04 17:58:50 +0000269
270# Test long header wrapping
Barry Warsaw41075852001-09-23 03:18:13 +0000271class TestLongHeaders(unittest.TestCase):
272 def test_header_splitter(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000273 msg = MIMEText('')
Barry Warsaw41075852001-09-23 03:18:13 +0000274 # It'd be great if we could use add_header() here, but that doesn't
275 # guarantee an order of the parameters.
276 msg['X-Foobar-Spoink-Defrobnit'] = (
277 'wasnipoop; giraffes="very-long-necked-animals"; '
278 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
279 sfp = StringIO()
280 g = Generator(sfp)
281 g(msg)
Barry Warsaw08a534d2001-10-04 17:58:50 +0000282 self.assertEqual(sfp.getvalue(), openfile('msg_18.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +0000283
Barry Warsaw07227d12001-10-17 20:52:26 +0000284 def test_no_semis_header_splitter(self):
285 msg = Message()
286 msg['From'] = 'test@dom.ain'
287 refparts = []
288 for i in range(10):
289 refparts.append('<%d@dom.ain>' % i)
290 msg['References'] = SPACE.join(refparts)
291 msg.set_payload('Test')
292 sfp = StringIO()
293 g = Generator(sfp)
294 g(msg)
295 self.assertEqual(sfp.getvalue(), """\
296From: test@dom.ain
297References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
Tim Peterse0c446b2001-10-18 21:57:37 +0000298\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
Barry Warsaw07227d12001-10-17 20:52:26 +0000299
300Test""")
301
302 def test_no_split_long_header(self):
303 msg = Message()
304 msg['From'] = 'test@dom.ain'
305 refparts = []
306 msg['References'] = 'x' * 80
307 msg.set_payload('Test')
308 sfp = StringIO()
309 g = Generator(sfp)
310 g(msg)
311 self.assertEqual(sfp.getvalue(), """\
312From: test@dom.ain
313References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
314
315Test""")
316
Barry Warsaw41075852001-09-23 03:18:13 +0000317
Barry Warsaw08a534d2001-10-04 17:58:50 +0000318
319# Test mangling of "From " lines in the body of a message
Barry Warsaw41075852001-09-23 03:18:13 +0000320class TestFromMangling(unittest.TestCase):
321 def setUp(self):
322 self.msg = Message()
323 self.msg['From'] = 'aaa@bbb.org'
324 self.msg.add_payload("""\
325From the desk of A.A.A.:
326Blah blah blah
327""")
328
329 def test_mangled_from(self):
330 s = StringIO()
331 g = Generator(s, mangle_from_=1)
332 g(self.msg)
333 self.assertEqual(s.getvalue(), """\
334From: aaa@bbb.org
335
336>From the desk of A.A.A.:
337Blah blah blah
338""")
339
340 def test_dont_mangle_from(self):
341 s = StringIO()
342 g = Generator(s, mangle_from_=0)
343 g(self.msg)
344 self.assertEqual(s.getvalue(), """\
345From: aaa@bbb.org
346
347From the desk of A.A.A.:
348Blah blah blah
349""")
350
351
Barry Warsaw08a534d2001-10-04 17:58:50 +0000352
Barry Warsawfee435a2001-10-09 19:23:57 +0000353# Test the basic MIMEAudio class
354class TestMIMEAudio(unittest.TestCase):
355 def setUp(self):
356 # In Python, audiotest.au lives in Lib/test not Lib/test/data
357 fp = open(findfile('audiotest.au'))
358 try:
359 self._audiodata = fp.read()
360 finally:
361 fp.close()
362 self._au = MIMEAudio(self._audiodata)
363
364 def test_guess_minor_type(self):
365 self.assertEqual(self._au.get_type(), 'audio/basic')
366
367 def test_encoding(self):
368 payload = self._au.get_payload()
369 self.assertEqual(base64.decodestring(payload), self._audiodata)
370
371 def checkSetMinor(self):
372 au = MIMEAudio(self._audiodata, 'fish')
373 self.assertEqual(im.get_type(), 'audio/fish')
374
375 def test_custom_encoder(self):
376 eq = self.assertEqual
377 def encoder(msg):
378 orig = msg.get_payload()
379 msg.set_payload(0)
380 msg['Content-Transfer-Encoding'] = 'broken64'
381 au = MIMEAudio(self._audiodata, _encoder=encoder)
382 eq(au.get_payload(), 0)
383 eq(au['content-transfer-encoding'], 'broken64')
384
385 def test_add_header(self):
386 eq = self.assertEqual
387 unless = self.failUnless
388 self._au.add_header('Content-Disposition', 'attachment',
389 filename='audiotest.au')
390 eq(self._au['content-disposition'],
391 'attachment; filename="audiotest.au"')
392 eq(self._au.get_params(header='content-disposition'),
393 [('attachment', ''), ('filename', 'audiotest.au')])
394 eq(self._au.get_param('filename', header='content-disposition'),
395 'audiotest.au')
396 missing = []
397 eq(self._au.get_param('attachment', header='content-disposition'), '')
398 unless(self._au.get_param('foo', failobj=missing,
399 header='content-disposition') is missing)
400 # Try some missing stuff
401 unless(self._au.get_param('foobar', missing) is missing)
402 unless(self._au.get_param('attachment', missing,
403 header='foobar') is missing)
404
405
406
Barry Warsaw65279d02001-09-26 05:47:08 +0000407# Test the basic MIMEImage class
408class TestMIMEImage(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000409 def setUp(self):
410 fp = openfile('PyBanner048.gif')
411 try:
412 self._imgdata = fp.read()
413 finally:
414 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000415 self._im = MIMEImage(self._imgdata)
Barry Warsaw41075852001-09-23 03:18:13 +0000416
417 def test_guess_minor_type(self):
418 self.assertEqual(self._im.get_type(), 'image/gif')
419
420 def test_encoding(self):
421 payload = self._im.get_payload()
422 self.assertEqual(base64.decodestring(payload), self._imgdata)
423
424 def checkSetMinor(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000425 im = MIMEImage(self._imgdata, 'fish')
Barry Warsaw41075852001-09-23 03:18:13 +0000426 self.assertEqual(im.get_type(), 'image/fish')
427
428 def test_custom_encoder(self):
429 eq = self.assertEqual
430 def encoder(msg):
431 orig = msg.get_payload()
432 msg.set_payload(0)
433 msg['Content-Transfer-Encoding'] = 'broken64'
Barry Warsaw65279d02001-09-26 05:47:08 +0000434 im = MIMEImage(self._imgdata, _encoder=encoder)
Barry Warsaw41075852001-09-23 03:18:13 +0000435 eq(im.get_payload(), 0)
436 eq(im['content-transfer-encoding'], 'broken64')
437
438 def test_add_header(self):
439 eq = self.assertEqual
440 unless = self.failUnless
441 self._im.add_header('Content-Disposition', 'attachment',
442 filename='dingusfish.gif')
443 eq(self._im['content-disposition'],
444 'attachment; filename="dingusfish.gif"')
445 eq(self._im.get_params(header='content-disposition'),
Barry Warsaw65279d02001-09-26 05:47:08 +0000446 [('attachment', ''), ('filename', 'dingusfish.gif')])
Barry Warsaw41075852001-09-23 03:18:13 +0000447 eq(self._im.get_param('filename', header='content-disposition'),
448 'dingusfish.gif')
449 missing = []
Barry Warsaw65279d02001-09-26 05:47:08 +0000450 eq(self._im.get_param('attachment', header='content-disposition'), '')
451 unless(self._im.get_param('foo', failobj=missing,
Barry Warsaw41075852001-09-23 03:18:13 +0000452 header='content-disposition') is missing)
453 # Try some missing stuff
454 unless(self._im.get_param('foobar', missing) is missing)
455 unless(self._im.get_param('attachment', missing,
456 header='foobar') is missing)
457
458
Barry Warsaw08a534d2001-10-04 17:58:50 +0000459
Barry Warsaw65279d02001-09-26 05:47:08 +0000460# Test the basic MIMEText class
461class TestMIMEText(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000462 def setUp(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000463 self._msg = MIMEText('hello there')
Barry Warsaw41075852001-09-23 03:18:13 +0000464
465 def test_types(self):
466 eq = self.assertEqual
467 unless = self.failUnless
468 eq(self._msg.get_type(), 'text/plain')
469 eq(self._msg.get_param('charset'), 'us-ascii')
470 missing = []
471 unless(self._msg.get_param('foobar', missing) is missing)
472 unless(self._msg.get_param('charset', missing, header='foobar')
473 is missing)
474
475 def test_payload(self):
476 self.assertEqual(self._msg.get_payload(), 'hello there\n')
477 self.failUnless(not self._msg.is_multipart())
478
479
Barry Warsaw08a534d2001-10-04 17:58:50 +0000480
481# Test a more complicated multipart/mixed type message
Barry Warsaw41075852001-09-23 03:18:13 +0000482class TestMultipartMixed(unittest.TestCase):
483 def setUp(self):
484 fp = openfile('PyBanner048.gif')
485 try:
486 data = fp.read()
487 finally:
488 fp.close()
489
490 container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
Barry Warsaw65279d02001-09-26 05:47:08 +0000491 image = MIMEImage(data, name='dingusfish.gif')
Barry Warsaw41075852001-09-23 03:18:13 +0000492 image.add_header('content-disposition', 'attachment',
493 filename='dingusfish.gif')
Barry Warsaw65279d02001-09-26 05:47:08 +0000494 intro = MIMEText('''\
Barry Warsaw41075852001-09-23 03:18:13 +0000495Hi there,
496
497This is the dingus fish.
498''')
499 container.add_payload(intro)
500 container.add_payload(image)
501 container['From'] = 'Barry <barry@digicool.com>'
502 container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
503 container['Subject'] = 'Here is your dingus fish'
Tim Peters527e64f2001-10-04 05:36:56 +0000504
Barry Warsaw41075852001-09-23 03:18:13 +0000505 now = 987809702.54848599
506 timetuple = time.localtime(now)
507 if timetuple[-1] == 0:
508 tzsecs = time.timezone
509 else:
510 tzsecs = time.altzone
511 if tzsecs > 0:
512 sign = '-'
513 else:
514 sign = '+'
515 tzoffset = ' %s%04d' % (sign, tzsecs / 36)
516 container['Date'] = time.strftime(
517 '%a, %d %b %Y %H:%M:%S',
518 time.localtime(now)) + tzoffset
519 self._msg = container
520 self._im = image
521 self._txt = intro
522
523 def test_hierarchy(self):
524 # convenience
525 eq = self.assertEqual
526 unless = self.failUnless
527 raises = self.assertRaises
528 # tests
529 m = self._msg
530 unless(m.is_multipart())
531 eq(m.get_type(), 'multipart/mixed')
532 eq(len(m.get_payload()), 2)
533 raises(IndexError, m.get_payload, 2)
534 m0 = m.get_payload(0)
535 m1 = m.get_payload(1)
536 unless(m0 is self._txt)
537 unless(m1 is self._im)
538 eq(m.get_payload(), [m0, m1])
539 unless(not m0.is_multipart())
540 unless(not m1.is_multipart())
541
542
Barry Warsaw08a534d2001-10-04 17:58:50 +0000543
544# Test some badly formatted messages
Barry Warsaw41075852001-09-23 03:18:13 +0000545class TestNonConformant(TestEmailBase):
546 def test_parse_missing_minor_type(self):
547 eq = self.assertEqual
548 msg = self._msgobj('msg_14.txt')
549 eq(msg.get_type(), 'text')
550 eq(msg.get_main_type(), 'text')
551 self.failUnless(msg.get_subtype() is None)
552
553 def test_bogus_boundary(self):
554 fp = openfile('msg_15.txt')
555 try:
556 data = fp.read()
557 finally:
558 fp.close()
559 p = Parser()
560 # Note, under a future non-strict parsing mode, this would parse the
561 # message into the intended message tree.
562 self.assertRaises(Errors.BoundaryError, p.parsestr, data)
563
564
Barry Warsaw08a534d2001-10-04 17:58:50 +0000565
566# Test RFC 2047 header encoding and decoding
Barry Warsaw41075852001-09-23 03:18:13 +0000567class TestRFC2047(unittest.TestCase):
568 def test_iso_8859_1(self):
569 eq = self.assertEqual
570 s = '=?iso-8859-1?q?this=20is=20some=20text?='
571 eq(Utils.decode(s), 'this is some text')
572 s = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?='
573 eq(Utils.decode(s), u'Keld_J\xf8rn_Simonsen')
574 s = '=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=' \
575 '=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
576 eq(Utils.decode(s), 'If you can read this you understand the example.')
577 s = '=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?='
578 eq(Utils.decode(s),
579 u'\u05dd\u05d5\u05dc\u05e9 \u05df\u05d1 \u05d9\u05dc\u05d8\u05e4\u05e0')
580 s = '=?iso-8859-1?q?this=20is?= =?iso-8859-1?q?some=20text?='
581 eq(Utils.decode(s), u'this is some text')
582
583 def test_encode_header(self):
584 eq = self.assertEqual
585 s = 'this is some text'
586 eq(Utils.encode(s), '=?iso-8859-1?q?this=20is=20some=20text?=')
587 s = 'Keld_J\xf8rn_Simonsen'
588 eq(Utils.encode(s), '=?iso-8859-1?q?Keld_J=F8rn_Simonsen?=')
589 s1 = 'If you can read this yo'
590 s2 = 'u understand the example.'
591 eq(Utils.encode(s1, encoding='b'),
592 '=?iso-8859-1?b?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=')
593 eq(Utils.encode(s2, charset='iso-8859-2', encoding='b'),
594 '=?iso-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=')
595
596
Barry Warsaw08a534d2001-10-04 17:58:50 +0000597
598# Test the MIMEMessage class
Barry Warsaw65279d02001-09-26 05:47:08 +0000599class TestMIMEMessage(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +0000600 def setUp(self):
601 fp = openfile('msg_11.txt')
602 self._text = fp.read()
603 fp.close()
604
605 def test_type_error(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000606 self.assertRaises(TypeError, MIMEMessage, 'a plain string')
Barry Warsaw41075852001-09-23 03:18:13 +0000607
608 def test_valid_argument(self):
609 eq = self.assertEqual
610 subject = 'A sub-message'
611 m = Message()
612 m['Subject'] = subject
Barry Warsaw65279d02001-09-26 05:47:08 +0000613 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000614 eq(r.get_type(), 'message/rfc822')
615 self.failUnless(r.get_payload() is m)
616 eq(r.get_payload()['subject'], subject)
617
618 def test_generate(self):
619 # First craft the message to be encapsulated
620 m = Message()
621 m['Subject'] = 'An enclosed message'
622 m.add_payload('Here is the body of the message.\n')
Barry Warsaw65279d02001-09-26 05:47:08 +0000623 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000624 r['Subject'] = 'The enclosing message'
625 s = StringIO()
626 g = Generator(s)
627 g(r)
Barry Warsaw65279d02001-09-26 05:47:08 +0000628 self.assertEqual(s.getvalue(), """\
629Content-Type: message/rfc822
630MIME-Version: 1.0
631Subject: The enclosing message
632
633Subject: An enclosed message
634
635Here is the body of the message.
636""")
637
638 def test_parse_message_rfc822(self):
639 eq = self.assertEqual
640 msg = self._msgobj('msg_11.txt')
641 eq(msg.get_type(), 'message/rfc822')
642 eq(len(msg.get_payload()), 1)
643 submsg = msg.get_payload()
644 self.failUnless(isinstance(submsg, Message))
645 eq(submsg['subject'], 'An enclosed message')
646 eq(submsg.get_payload(), 'Here is the body of the message.\n')
647
648 def test_dsn(self):
649 eq = self.assertEqual
650 unless = self.failUnless
651 # msg 16 is a Delivery Status Notification, see RFC XXXX
652 msg = self._msgobj('msg_16.txt')
653 eq(msg.get_type(), 'multipart/report')
654 unless(msg.is_multipart())
655 eq(len(msg.get_payload()), 3)
656 # Subpart 1 is a text/plain, human readable section
657 subpart = msg.get_payload(0)
658 eq(subpart.get_type(), 'text/plain')
659 eq(subpart.get_payload(), """\
660This report relates to a message you sent with the following header fields:
661
662 Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
663 Date: Sun, 23 Sep 2001 20:10:55 -0700
664 From: "Ian T. Henry" <henryi@oxy.edu>
665 To: SoCal Raves <scr@socal-raves.org>
666 Subject: [scr] yeah for Ians!!
667
668Your message cannot be delivered to the following recipients:
669
670 Recipient address: jangel1@cougar.noc.ucla.edu
671 Reason: recipient reached disk quota
672
673""")
674 # Subpart 2 contains the machine parsable DSN information. It
675 # consists of two blocks of headers, represented by two nested Message
676 # objects.
677 subpart = msg.get_payload(1)
678 eq(subpart.get_type(), 'message/delivery-status')
679 eq(len(subpart.get_payload()), 2)
680 # message/delivery-status should treat each block as a bunch of
681 # headers, i.e. a bunch of Message objects.
682 dsn1 = subpart.get_payload(0)
683 unless(isinstance(dsn1, Message))
684 eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu')
685 eq(dsn1.get_param('dns', header='reporting-mta'), '')
686 # Try a missing one <wink>
687 eq(dsn1.get_param('nsd', header='reporting-mta'), None)
688 dsn2 = subpart.get_payload(1)
689 unless(isinstance(dsn2, Message))
690 eq(dsn2['action'], 'failed')
691 eq(dsn2.get_params(header='original-recipient'),
692 [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')])
693 eq(dsn2.get_param('rfc822', header='final-recipient'), '')
694 # Subpart 3 is the original message
695 subpart = msg.get_payload(2)
696 eq(subpart.get_type(), 'message/rfc822')
697 subsubpart = subpart.get_payload()
698 unless(isinstance(subsubpart, Message))
699 eq(subsubpart.get_type(), 'text/plain')
700 eq(subsubpart['message-id'],
701 '<002001c144a6$8752e060$56104586@oxy.edu>')
Barry Warsaw41075852001-09-23 03:18:13 +0000702
Barry Warsaw1f0fa922001-10-19 04:08:59 +0000703 def test_epilogue(self):
704 fp = openfile('msg_21.txt')
705 try:
706 text = fp.read()
707 finally:
708 fp.close()
709 msg = Message()
710 msg['From'] = 'aperson@dom.ain'
711 msg['To'] = 'bperson@dom.ain'
712 msg['Subject'] = 'Test'
713 msg.preamble = 'MIME message\n'
714 msg.epilogue = 'End of MIME message\n'
715 msg1 = MIMEText('One')
716 msg2 = MIMEText('Two')
717 msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
718 msg.add_payload(msg1)
719 msg.add_payload(msg2)
720 sfp = StringIO()
721 g = Generator(sfp)
722 g(msg)
723 self.assertEqual(sfp.getvalue(), text)
724
Barry Warsaw41075852001-09-23 03:18:13 +0000725
Barry Warsaw08a534d2001-10-04 17:58:50 +0000726
727# A general test of parser->model->generator idempotency. IOW, read a message
728# in, parse it into a message object tree, then without touching the tree,
729# regenerate the plain text. The original text and the transformed text
730# should be identical. Note: that we ignore the Unix-From since that may
731# contain a changed date.
Barry Warsaw41075852001-09-23 03:18:13 +0000732class TestIdempotent(unittest.TestCase):
733 def _msgobj(self, filename):
734 fp = openfile(filename)
735 try:
736 data = fp.read()
737 finally:
738 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000739 msg = email.message_from_string(data)
740 return msg, data
Barry Warsaw41075852001-09-23 03:18:13 +0000741
742 def _idempotent(self, msg, text):
743 eq = self.assertEquals
744 s = StringIO()
745 g = Generator(s, maxheaderlen=0)
746 g(msg)
747 eq(text, s.getvalue())
748
749 def test_parse_text_message(self):
750 eq = self.assertEquals
751 msg, text = self._msgobj('msg_01.txt')
752 eq(msg.get_type(), 'text/plain')
753 eq(msg.get_main_type(), 'text')
754 eq(msg.get_subtype(), 'plain')
Barry Warsaw65279d02001-09-26 05:47:08 +0000755 eq(msg.get_params()[1], ('charset', 'us-ascii'))
Barry Warsaw41075852001-09-23 03:18:13 +0000756 eq(msg.get_param('charset'), 'us-ascii')
757 eq(msg.preamble, None)
758 eq(msg.epilogue, None)
759 self._idempotent(msg, text)
760
761 def test_parse_untyped_message(self):
762 eq = self.assertEquals
763 msg, text = self._msgobj('msg_03.txt')
764 eq(msg.get_type(), None)
765 eq(msg.get_params(), None)
766 eq(msg.get_param('charset'), None)
767 self._idempotent(msg, text)
768
769 def test_simple_multipart(self):
770 msg, text = self._msgobj('msg_04.txt')
771 self._idempotent(msg, text)
772
773 def test_MIME_digest(self):
774 msg, text = self._msgobj('msg_02.txt')
775 self._idempotent(msg, text)
776
777 def test_mixed_with_image(self):
778 msg, text = self._msgobj('msg_06.txt')
779 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +0000780
Barry Warsaw41075852001-09-23 03:18:13 +0000781 def test_multipart_report(self):
782 msg, text = self._msgobj('msg_05.txt')
783 self._idempotent(msg, text)
Barry Warsaw65279d02001-09-26 05:47:08 +0000784
785 def test_dsn(self):
786 msg, text = self._msgobj('msg_16.txt')
787 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +0000788
Barry Warsaw1f0fa922001-10-19 04:08:59 +0000789 def test_preamble_epilogue(self):
790 msg, text = self._msgobj('msg_21.txt')
791 self._idempotent(msg, text)
792
Barry Warsaw41075852001-09-23 03:18:13 +0000793 def test_content_type(self):
794 eq = self.assertEquals
795 # Get a message object and reset the seek pointer for other tests
796 msg, text = self._msgobj('msg_05.txt')
797 eq(msg.get_type(), 'multipart/report')
798 # Test the Content-Type: parameters
799 params = {}
Barry Warsaw65279d02001-09-26 05:47:08 +0000800 for pk, pv in msg.get_params():
Barry Warsaw41075852001-09-23 03:18:13 +0000801 params[pk] = pv
802 eq(params['report-type'], 'delivery-status')
Barry Warsaw65279d02001-09-26 05:47:08 +0000803 eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
Barry Warsaw41075852001-09-23 03:18:13 +0000804 eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
805 eq(msg.epilogue, '\n\n')
806 eq(len(msg.get_payload()), 3)
807 # Make sure the subparts are what we expect
808 msg1 = msg.get_payload(0)
809 eq(msg1.get_type(), 'text/plain')
810 eq(msg1.get_payload(), 'Yadda yadda yadda\n')
811 msg2 = msg.get_payload(1)
812 eq(msg2.get_type(), None)
813 eq(msg2.get_payload(), 'Yadda yadda yadda\n')
814 msg3 = msg.get_payload(2)
815 eq(msg3.get_type(), 'message/rfc822')
816 self.failUnless(isinstance(msg3, Message))
817 msg4 = msg3.get_payload()
818 self.failUnless(isinstance(msg4, Message))
819 eq(msg4.get_payload(), 'Yadda yadda yadda\n')
820
821 def test_parser(self):
822 eq = self.assertEquals
823 msg, text = self._msgobj('msg_06.txt')
824 # Check some of the outer headers
825 eq(msg.get_type(), 'message/rfc822')
826 # Make sure there's exactly one thing in the payload and that's a
827 # sub-Message object of type text/plain
828 msg1 = msg.get_payload()
829 self.failUnless(isinstance(msg1, Message))
830 eq(msg1.get_type(), 'text/plain')
831 self.failUnless(isinstance(msg1.get_payload(), StringType))
832 eq(msg1.get_payload(), '\n')
Barry Warsaw41075852001-09-23 03:18:13 +0000833
Tim Peters527e64f2001-10-04 05:36:56 +0000834
Barry Warsaw08a534d2001-10-04 17:58:50 +0000835
836# Test various other bits of the package's functionality
Barry Warsaw41075852001-09-23 03:18:13 +0000837class TestMiscellaneous(unittest.TestCase):
838 def test_message_from_string(self):
839 fp = openfile('msg_01.txt')
840 try:
841 text = fp.read()
842 finally:
843 fp.close()
844 msg = email.message_from_string(text)
845 s = StringIO()
846 # Don't wrap/continue long headers since we're trying to test
847 # idempotency.
848 g = Generator(s, maxheaderlen=0)
849 g(msg)
850 self.assertEqual(text, s.getvalue())
851
852 def test_message_from_file(self):
853 fp = openfile('msg_01.txt')
854 try:
855 text = fp.read()
856 fp.seek(0)
857 msg = email.message_from_file(fp)
858 s = StringIO()
859 # Don't wrap/continue long headers since we're trying to test
860 # idempotency.
861 g = Generator(s, maxheaderlen=0)
862 g(msg)
863 self.assertEqual(text, s.getvalue())
864 finally:
865 fp.close()
866
867 def test_message_from_string_with_class(self):
868 unless = self.failUnless
869 fp = openfile('msg_01.txt')
870 try:
871 text = fp.read()
872 finally:
873 fp.close()
874 # Create a subclass
875 class MyMessage(Message):
876 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000877
Barry Warsaw41075852001-09-23 03:18:13 +0000878 msg = email.message_from_string(text, MyMessage)
879 unless(isinstance(msg, MyMessage))
880 # Try something more complicated
881 fp = openfile('msg_02.txt')
882 try:
883 text = fp.read()
884 finally:
885 fp.close()
886 msg = email.message_from_string(text, MyMessage)
887 for subpart in msg.walk():
888 unless(isinstance(subpart, MyMessage))
889
Barry Warsaw41075852001-09-23 03:18:13 +0000890 def test_message_from_file_with_class(self):
891 unless = self.failUnless
892 # Create a subclass
893 class MyMessage(Message):
894 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000895
Barry Warsaw41075852001-09-23 03:18:13 +0000896 fp = openfile('msg_01.txt')
897 try:
898 msg = email.message_from_file(fp, MyMessage)
899 finally:
900 fp.close()
901 unless(isinstance(msg, MyMessage))
902 # Try something more complicated
903 fp = openfile('msg_02.txt')
904 try:
905 msg = email.message_from_file(fp, MyMessage)
906 finally:
907 fp.close()
908 for subpart in msg.walk():
909 unless(isinstance(subpart, MyMessage))
910
Barry Warsawfee435a2001-10-09 19:23:57 +0000911 def test__all__(self):
912 module = __import__('email')
913 all = module.__all__
914 all.sort()
915 self.assertEqual(all, ['Encoders', 'Errors', 'Generator', 'Iterators',
916 'MIMEAudio', 'MIMEBase', 'MIMEImage',
917 'MIMEMessage', 'MIMEText', 'Message', 'Parser',
918 'Utils',
919 'message_from_file', 'message_from_string'])
920
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000921 def test_formatdate(self):
922 now = 1005327232.109884
Barry Warsawe2748642001-11-27 07:12:35 +0000923 gm_epoch = time.gmtime(0)[0:3]
924 loc_epoch = time.localtime(0)[0:3]
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000925 # When does the epoch start?
Barry Warsawe2748642001-11-27 07:12:35 +0000926 if gm_epoch == (1970, 1, 1):
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000927 # traditional Unix epoch
928 matchdate = 'Fri, 09 Nov 2001 17:33:52 -0000'
Barry Warsawe2748642001-11-27 07:12:35 +0000929 elif loc_epoch == (1904, 1, 1):
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000930 # Mac epoch
931 matchdate = 'Sat, 09 Nov 1935 16:33:52 -0000'
932 else:
933 matchdate = "I don't understand your epoch"
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000934 gdate = Utils.formatdate(now)
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000935 self.assertEqual(gdate, matchdate)
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000936
Barry Warsaw4586d2c2001-11-19 18:38:42 +0000937 def test_formatdate_localtime(self):
Barry Warsaw75a40fc2001-11-19 16:31:06 +0000938 now = 1005327232.109884
939 ldate = Utils.formatdate(now, localtime=1)
940 zone = ldate.split()[5]
Barry Warsaw4586d2c2001-11-19 18:38:42 +0000941 offset = int(zone[1:3]) * 3600 + int(zone[-2:]) * 60
942 # Remember offset is in seconds west of UTC, but the timezone is in
943 # minutes east of UTC, so the signs differ.
944 if zone[0] == '+':
945 offset = -offset
Barry Warsaw75a40fc2001-11-19 16:31:06 +0000946 if time.daylight and time.localtime(now)[-1]:
947 toff = time.altzone
948 else:
949 toff = time.timezone
950 self.assertEqual(offset, toff)
951
952 def test_parsedate_none(self):
Barry Warsaw19c10ca2001-11-13 18:01:37 +0000953 self.assertEqual(Utils.parsedate(''), None)
954
Barry Warsaweae36ac2001-12-20 16:37:27 +0000955 def test_parseaddr_empty(self):
956 self.assertEqual(Utils.parseaddr('<>'), ('', ''))
957 self.assertEqual(Utils.dump_address_pair(Utils.parseaddr('<>')), '')
958
Barry Warsaw41075852001-09-23 03:18:13 +0000959
Barry Warsaw08a534d2001-10-04 17:58:50 +0000960
961# Test the iterator/generators
Barry Warsaw41075852001-09-23 03:18:13 +0000962class TestIterators(TestEmailBase):
963 def test_body_line_iterator(self):
964 eq = self.assertEqual
965 # First a simple non-multipart message
966 msg = self._msgobj('msg_01.txt')
967 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000968 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000969 eq(len(lines), 6)
970 eq(EMPTYSTRING.join(lines), msg.get_payload())
971 # Now a more complicated multipart
972 msg = self._msgobj('msg_02.txt')
973 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000974 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000975 eq(len(lines), 43)
Barry Warsaw08a534d2001-10-04 17:58:50 +0000976 eq(EMPTYSTRING.join(lines), openfile('msg_19.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +0000977
978 def test_typed_subpart_iterator(self):
979 eq = self.assertEqual
980 msg = self._msgobj('msg_04.txt')
981 it = Iterators.typed_subpart_iterator(msg, 'text')
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000982 lines = [subpart.get_payload() for subpart in it]
983 eq(len(lines), 2)
Barry Warsaw41075852001-09-23 03:18:13 +0000984 eq(EMPTYSTRING.join(lines), """\
985a simple kind of mirror
986to reflect upon our own
987a simple kind of mirror
988to reflect upon our own
989""")
990
Barry Warsawcdc632c2001-10-15 04:39:02 +0000991 def test_typed_subpart_iterator_default_type(self):
992 eq = self.assertEqual
993 msg = self._msgobj('msg_03.txt')
994 it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
995 lines = []
996 subparts = 0
997 for subpart in it:
998 subparts += 1
999 lines.append(subpart.get_payload())
1000 eq(subparts, 1)
1001 eq(EMPTYSTRING.join(lines), """\
1002
1003Hi,
1004
1005Do you like this message?
1006
1007-Me
1008""")
Barry Warsaw41075852001-09-23 03:18:13 +00001009
Barry Warsaw08a534d2001-10-04 17:58:50 +00001010
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001011class TestParsers(unittest.TestCase):
1012 def test_header_parser(self):
1013 eq = self.assertEqual
1014 # Parse only the headers of a complex multipart MIME document
1015 p = HeaderParser()
1016 fp = openfile('msg_02.txt')
1017 msg = p.parse(fp)
1018 eq(msg['from'], 'ppp-request@zzz.org')
1019 eq(msg['to'], 'ppp@zzz.org')
1020 eq(msg.get_type(), 'multipart/mixed')
1021 eq(msg.is_multipart(), 0)
1022 self.failUnless(isinstance(msg.get_payload(), StringType))
1023
1024
1025
Barry Warsaw41075852001-09-23 03:18:13 +00001026def suite():
1027 suite = unittest.TestSuite()
1028 suite.addTest(unittest.makeSuite(TestMessageAPI))
1029 suite.addTest(unittest.makeSuite(TestEncoders))
1030 suite.addTest(unittest.makeSuite(TestLongHeaders))
1031 suite.addTest(unittest.makeSuite(TestFromMangling))
Barry Warsawfee435a2001-10-09 19:23:57 +00001032 suite.addTest(unittest.makeSuite(TestMIMEAudio))
Barry Warsaw65279d02001-09-26 05:47:08 +00001033 suite.addTest(unittest.makeSuite(TestMIMEImage))
1034 suite.addTest(unittest.makeSuite(TestMIMEText))
Barry Warsaw41075852001-09-23 03:18:13 +00001035 suite.addTest(unittest.makeSuite(TestMultipartMixed))
1036 suite.addTest(unittest.makeSuite(TestNonConformant))
1037 suite.addTest(unittest.makeSuite(TestRFC2047))
Barry Warsaw65279d02001-09-26 05:47:08 +00001038 suite.addTest(unittest.makeSuite(TestMIMEMessage))
Barry Warsaw41075852001-09-23 03:18:13 +00001039 suite.addTest(unittest.makeSuite(TestIdempotent))
1040 suite.addTest(unittest.makeSuite(TestMiscellaneous))
1041 suite.addTest(unittest.makeSuite(TestIterators))
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001042 suite.addTest(unittest.makeSuite(TestParsers))
Barry Warsaw41075852001-09-23 03:18:13 +00001043 return suite
1044
1045
Barry Warsaw08a534d2001-10-04 17:58:50 +00001046
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001047def test_main():
Barry Warsaw41075852001-09-23 03:18:13 +00001048 from test_support import run_suite
1049 run_suite(suite())
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001050
1051if __name__ == '__main__':
1052 test_main()