blob: 7105f7d4b31b315cb2f57f03aaa65d7a14106b01 [file] [log] [blame]
Barry Warsaw763af412002-01-27 06:48:47 +00001# Copyright (C) 2001,2002 Python Software Foundation
Barry Warsaw41075852001-09-23 03:18:13 +00002# 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 Warsaw763af412002-01-27 06:48:47 +0000793 def test_multipart_one_part(self):
794 msg, text = self._msgobj('msg_23.txt')
795 self._idempotent(msg, text)
796
Barry Warsaw41075852001-09-23 03:18:13 +0000797 def test_content_type(self):
798 eq = self.assertEquals
799 # Get a message object and reset the seek pointer for other tests
800 msg, text = self._msgobj('msg_05.txt')
801 eq(msg.get_type(), 'multipart/report')
802 # Test the Content-Type: parameters
803 params = {}
Barry Warsaw65279d02001-09-26 05:47:08 +0000804 for pk, pv in msg.get_params():
Barry Warsaw41075852001-09-23 03:18:13 +0000805 params[pk] = pv
806 eq(params['report-type'], 'delivery-status')
Barry Warsaw65279d02001-09-26 05:47:08 +0000807 eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
Barry Warsaw41075852001-09-23 03:18:13 +0000808 eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
809 eq(msg.epilogue, '\n\n')
810 eq(len(msg.get_payload()), 3)
811 # Make sure the subparts are what we expect
812 msg1 = msg.get_payload(0)
813 eq(msg1.get_type(), 'text/plain')
814 eq(msg1.get_payload(), 'Yadda yadda yadda\n')
815 msg2 = msg.get_payload(1)
816 eq(msg2.get_type(), None)
817 eq(msg2.get_payload(), 'Yadda yadda yadda\n')
818 msg3 = msg.get_payload(2)
819 eq(msg3.get_type(), 'message/rfc822')
820 self.failUnless(isinstance(msg3, Message))
821 msg4 = msg3.get_payload()
822 self.failUnless(isinstance(msg4, Message))
823 eq(msg4.get_payload(), 'Yadda yadda yadda\n')
824
825 def test_parser(self):
826 eq = self.assertEquals
827 msg, text = self._msgobj('msg_06.txt')
828 # Check some of the outer headers
829 eq(msg.get_type(), 'message/rfc822')
830 # Make sure there's exactly one thing in the payload and that's a
831 # sub-Message object of type text/plain
832 msg1 = msg.get_payload()
833 self.failUnless(isinstance(msg1, Message))
834 eq(msg1.get_type(), 'text/plain')
835 self.failUnless(isinstance(msg1.get_payload(), StringType))
836 eq(msg1.get_payload(), '\n')
Barry Warsaw41075852001-09-23 03:18:13 +0000837
Tim Peters527e64f2001-10-04 05:36:56 +0000838
Barry Warsaw08a534d2001-10-04 17:58:50 +0000839
840# Test various other bits of the package's functionality
Barry Warsaw41075852001-09-23 03:18:13 +0000841class TestMiscellaneous(unittest.TestCase):
842 def test_message_from_string(self):
843 fp = openfile('msg_01.txt')
844 try:
845 text = fp.read()
846 finally:
847 fp.close()
848 msg = email.message_from_string(text)
849 s = StringIO()
850 # Don't wrap/continue long headers since we're trying to test
851 # idempotency.
852 g = Generator(s, maxheaderlen=0)
853 g(msg)
854 self.assertEqual(text, s.getvalue())
855
856 def test_message_from_file(self):
857 fp = openfile('msg_01.txt')
858 try:
859 text = fp.read()
860 fp.seek(0)
861 msg = email.message_from_file(fp)
862 s = StringIO()
863 # Don't wrap/continue long headers since we're trying to test
864 # idempotency.
865 g = Generator(s, maxheaderlen=0)
866 g(msg)
867 self.assertEqual(text, s.getvalue())
868 finally:
869 fp.close()
870
871 def test_message_from_string_with_class(self):
872 unless = self.failUnless
873 fp = openfile('msg_01.txt')
874 try:
875 text = fp.read()
876 finally:
877 fp.close()
878 # Create a subclass
879 class MyMessage(Message):
880 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000881
Barry Warsaw41075852001-09-23 03:18:13 +0000882 msg = email.message_from_string(text, MyMessage)
883 unless(isinstance(msg, MyMessage))
884 # Try something more complicated
885 fp = openfile('msg_02.txt')
886 try:
887 text = fp.read()
888 finally:
889 fp.close()
890 msg = email.message_from_string(text, MyMessage)
891 for subpart in msg.walk():
892 unless(isinstance(subpart, MyMessage))
893
Barry Warsaw41075852001-09-23 03:18:13 +0000894 def test_message_from_file_with_class(self):
895 unless = self.failUnless
896 # Create a subclass
897 class MyMessage(Message):
898 pass
Tim Peters527e64f2001-10-04 05:36:56 +0000899
Barry Warsaw41075852001-09-23 03:18:13 +0000900 fp = openfile('msg_01.txt')
901 try:
902 msg = email.message_from_file(fp, MyMessage)
903 finally:
904 fp.close()
905 unless(isinstance(msg, MyMessage))
906 # Try something more complicated
907 fp = openfile('msg_02.txt')
908 try:
909 msg = email.message_from_file(fp, MyMessage)
910 finally:
911 fp.close()
912 for subpart in msg.walk():
913 unless(isinstance(subpart, MyMessage))
914
Barry Warsawfee435a2001-10-09 19:23:57 +0000915 def test__all__(self):
916 module = __import__('email')
917 all = module.__all__
918 all.sort()
919 self.assertEqual(all, ['Encoders', 'Errors', 'Generator', 'Iterators',
920 'MIMEAudio', 'MIMEBase', 'MIMEImage',
921 'MIMEMessage', 'MIMEText', 'Message', 'Parser',
922 'Utils',
923 'message_from_file', 'message_from_string'])
924
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000925 def test_formatdate(self):
926 now = 1005327232.109884
Barry Warsawe2748642001-11-27 07:12:35 +0000927 gm_epoch = time.gmtime(0)[0:3]
928 loc_epoch = time.localtime(0)[0:3]
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000929 # When does the epoch start?
Barry Warsawe2748642001-11-27 07:12:35 +0000930 if gm_epoch == (1970, 1, 1):
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000931 # traditional Unix epoch
932 matchdate = 'Fri, 09 Nov 2001 17:33:52 -0000'
Barry Warsawe2748642001-11-27 07:12:35 +0000933 elif loc_epoch == (1904, 1, 1):
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000934 # Mac epoch
935 matchdate = 'Sat, 09 Nov 1935 16:33:52 -0000'
936 else:
937 matchdate = "I don't understand your epoch"
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000938 gdate = Utils.formatdate(now)
Barry Warsaw7a1bea62001-11-18 23:15:58 +0000939 self.assertEqual(gdate, matchdate)
Barry Warsaw75edc6a2001-11-09 17:46:17 +0000940
Barry Warsaw4586d2c2001-11-19 18:38:42 +0000941 def test_formatdate_localtime(self):
Barry Warsaw75a40fc2001-11-19 16:31:06 +0000942 now = 1005327232.109884
943 ldate = Utils.formatdate(now, localtime=1)
944 zone = ldate.split()[5]
Barry Warsaw4586d2c2001-11-19 18:38:42 +0000945 offset = int(zone[1:3]) * 3600 + int(zone[-2:]) * 60
946 # Remember offset is in seconds west of UTC, but the timezone is in
947 # minutes east of UTC, so the signs differ.
948 if zone[0] == '+':
949 offset = -offset
Barry Warsaw75a40fc2001-11-19 16:31:06 +0000950 if time.daylight and time.localtime(now)[-1]:
951 toff = time.altzone
952 else:
953 toff = time.timezone
954 self.assertEqual(offset, toff)
955
956 def test_parsedate_none(self):
Barry Warsaw19c10ca2001-11-13 18:01:37 +0000957 self.assertEqual(Utils.parsedate(''), None)
958
Barry Warsaweae36ac2001-12-20 16:37:27 +0000959 def test_parseaddr_empty(self):
960 self.assertEqual(Utils.parseaddr('<>'), ('', ''))
961 self.assertEqual(Utils.dump_address_pair(Utils.parseaddr('<>')), '')
962
Barry Warsaw41075852001-09-23 03:18:13 +0000963
Barry Warsaw08a534d2001-10-04 17:58:50 +0000964
965# Test the iterator/generators
Barry Warsaw41075852001-09-23 03:18:13 +0000966class TestIterators(TestEmailBase):
967 def test_body_line_iterator(self):
968 eq = self.assertEqual
969 # First a simple non-multipart message
970 msg = self._msgobj('msg_01.txt')
971 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000972 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000973 eq(len(lines), 6)
974 eq(EMPTYSTRING.join(lines), msg.get_payload())
975 # Now a more complicated multipart
976 msg = self._msgobj('msg_02.txt')
977 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000978 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +0000979 eq(len(lines), 43)
Barry Warsaw08a534d2001-10-04 17:58:50 +0000980 eq(EMPTYSTRING.join(lines), openfile('msg_19.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +0000981
982 def test_typed_subpart_iterator(self):
983 eq = self.assertEqual
984 msg = self._msgobj('msg_04.txt')
985 it = Iterators.typed_subpart_iterator(msg, 'text')
Barry Warsawd1de6ea2001-10-04 18:18:37 +0000986 lines = [subpart.get_payload() for subpart in it]
987 eq(len(lines), 2)
Barry Warsaw41075852001-09-23 03:18:13 +0000988 eq(EMPTYSTRING.join(lines), """\
989a simple kind of mirror
990to reflect upon our own
991a simple kind of mirror
992to reflect upon our own
993""")
994
Barry Warsawcdc632c2001-10-15 04:39:02 +0000995 def test_typed_subpart_iterator_default_type(self):
996 eq = self.assertEqual
997 msg = self._msgobj('msg_03.txt')
998 it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
999 lines = []
1000 subparts = 0
1001 for subpart in it:
1002 subparts += 1
1003 lines.append(subpart.get_payload())
1004 eq(subparts, 1)
1005 eq(EMPTYSTRING.join(lines), """\
1006
1007Hi,
1008
1009Do you like this message?
1010
1011-Me
1012""")
Barry Warsaw41075852001-09-23 03:18:13 +00001013
Barry Warsaw08a534d2001-10-04 17:58:50 +00001014
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001015class TestParsers(unittest.TestCase):
1016 def test_header_parser(self):
1017 eq = self.assertEqual
1018 # Parse only the headers of a complex multipart MIME document
1019 p = HeaderParser()
1020 fp = openfile('msg_02.txt')
1021 msg = p.parse(fp)
1022 eq(msg['from'], 'ppp-request@zzz.org')
1023 eq(msg['to'], 'ppp@zzz.org')
1024 eq(msg.get_type(), 'multipart/mixed')
1025 eq(msg.is_multipart(), 0)
1026 self.failUnless(isinstance(msg.get_payload(), StringType))
1027
1028
1029
Barry Warsaw41075852001-09-23 03:18:13 +00001030def suite():
1031 suite = unittest.TestSuite()
1032 suite.addTest(unittest.makeSuite(TestMessageAPI))
1033 suite.addTest(unittest.makeSuite(TestEncoders))
1034 suite.addTest(unittest.makeSuite(TestLongHeaders))
1035 suite.addTest(unittest.makeSuite(TestFromMangling))
Barry Warsawfee435a2001-10-09 19:23:57 +00001036 suite.addTest(unittest.makeSuite(TestMIMEAudio))
Barry Warsaw65279d02001-09-26 05:47:08 +00001037 suite.addTest(unittest.makeSuite(TestMIMEImage))
1038 suite.addTest(unittest.makeSuite(TestMIMEText))
Barry Warsaw41075852001-09-23 03:18:13 +00001039 suite.addTest(unittest.makeSuite(TestMultipartMixed))
1040 suite.addTest(unittest.makeSuite(TestNonConformant))
1041 suite.addTest(unittest.makeSuite(TestRFC2047))
Barry Warsaw65279d02001-09-26 05:47:08 +00001042 suite.addTest(unittest.makeSuite(TestMIMEMessage))
Barry Warsaw41075852001-09-23 03:18:13 +00001043 suite.addTest(unittest.makeSuite(TestIdempotent))
1044 suite.addTest(unittest.makeSuite(TestMiscellaneous))
1045 suite.addTest(unittest.makeSuite(TestIterators))
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001046 suite.addTest(unittest.makeSuite(TestParsers))
Barry Warsaw41075852001-09-23 03:18:13 +00001047 return suite
1048
1049
Barry Warsaw08a534d2001-10-04 17:58:50 +00001050
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001051def test_main():
Barry Warsaw41075852001-09-23 03:18:13 +00001052 from test_support import run_suite
1053 run_suite(suite())
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001054
1055if __name__ == '__main__':
1056 test_main()