blob: b617bc0d7457e625f6a80c974b18fbbeaf294c89 [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
Barry Warsaw409a4c02002-04-10 21:01:31 +00004import sys
Barry Warsaw41075852001-09-23 03:18:13 +00005import os
6import time
7import unittest
8import base64
Barry Warsawb6a92132002-06-28 23:49:33 +00009import difflib
Barry Warsaw41075852001-09-23 03:18:13 +000010from cStringIO import StringIO
Barry Warsaw2c685062002-06-02 19:09:27 +000011from types import StringType, ListType
Barry Warsaw409a4c02002-04-10 21:01:31 +000012import warnings
Barry Warsaw41075852001-09-23 03:18:13 +000013
14import email
15
Barry Warsaw409a4c02002-04-10 21:01:31 +000016from email.Charset import Charset
17from email.Header import Header, decode_header
Barry Warsawbf7a59d2001-10-11 15:44:50 +000018from email.Parser import Parser, HeaderParser
Barry Warsaw41075852001-09-23 03:18:13 +000019from email.Generator import Generator, DecodedGenerator
20from email.Message import Message
Barry Warsawfee435a2001-10-09 19:23:57 +000021from email.MIMEAudio import MIMEAudio
Barry Warsaw65279d02001-09-26 05:47:08 +000022from email.MIMEText import MIMEText
23from email.MIMEImage import MIMEImage
Barry Warsaw41075852001-09-23 03:18:13 +000024from email.MIMEBase import MIMEBase
Barry Warsaw65279d02001-09-26 05:47:08 +000025from email.MIMEMessage import MIMEMessage
Barry Warsaw41075852001-09-23 03:18:13 +000026from email import Utils
27from email import Errors
28from email import Encoders
29from email import Iterators
Barry Warsaw409a4c02002-04-10 21:01:31 +000030from email import base64MIME
31from email import quopriMIME
Barry Warsaw41075852001-09-23 03:18:13 +000032
Barry Warsawc9ad32c2002-04-15 22:14:06 +000033import test_support
Guido van Rossum78f0dd32001-12-07 21:07:08 +000034from test_support import findfile, __file__ as test_support_file
Barry Warsaw41075852001-09-23 03:18:13 +000035
Barry Warsawc9ad32c2002-04-15 22:14:06 +000036
Barry Warsaw41075852001-09-23 03:18:13 +000037NL = '\n'
38EMPTYSTRING = ''
Barry Warsaw07227d12001-10-17 20:52:26 +000039SPACE = ' '
Barry Warsaw41075852001-09-23 03:18:13 +000040
Barry Warsaw409a4c02002-04-10 21:01:31 +000041# We don't care about DeprecationWarnings
42warnings.filterwarnings('ignore', '', DeprecationWarning, __name__)
43
Barry Warsaw41075852001-09-23 03:18:13 +000044
Barry Warsaw08a534d2001-10-04 17:58:50 +000045
Barry Warsaw41075852001-09-23 03:18:13 +000046def openfile(filename):
Guido van Rossum78f0dd32001-12-07 21:07:08 +000047 path = os.path.join(os.path.dirname(test_support_file), 'data', filename)
Barry Warsaw41075852001-09-23 03:18:13 +000048 return open(path)
49
50
Barry Warsaw08a534d2001-10-04 17:58:50 +000051
Barry Warsaw41075852001-09-23 03:18:13 +000052# Base test class
53class TestEmailBase(unittest.TestCase):
Barry Warsawb6a92132002-06-28 23:49:33 +000054 def ndiffAssertEqual(self, first, second):
55 """Like failUnlessEqual except use ndiff to produce readable output."""
56 if first <> second:
57 diff = difflib.ndiff(first.splitlines(), second.splitlines())
58 fp = StringIO()
59 print >> fp, NL, NL.join(diff)
60 raise self.failureException, fp.getvalue()
61
Barry Warsaw41075852001-09-23 03:18:13 +000062 def _msgobj(self, filename):
Barry Warsaw409a4c02002-04-10 21:01:31 +000063 fp = openfile(findfile(filename))
Barry Warsaw41075852001-09-23 03:18:13 +000064 try:
Barry Warsaw65279d02001-09-26 05:47:08 +000065 msg = email.message_from_file(fp)
Barry Warsaw41075852001-09-23 03:18:13 +000066 finally:
67 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +000068 return msg
Barry Warsaw41075852001-09-23 03:18:13 +000069
70
Barry Warsaw08a534d2001-10-04 17:58:50 +000071
Barry Warsaw41075852001-09-23 03:18:13 +000072# Test various aspects of the Message class's API
73class TestMessageAPI(TestEmailBase):
Barry Warsaw2f6a0b02001-10-09 15:49:35 +000074 def test_get_all(self):
75 eq = self.assertEqual
76 msg = self._msgobj('msg_20.txt')
77 eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
78 eq(msg.get_all('xx', 'n/a'), 'n/a')
79
Barry Warsaw409a4c02002-04-10 21:01:31 +000080 def test_getset_charset(self):
81 eq = self.assertEqual
82 msg = Message()
83 eq(msg.get_charset(), None)
84 charset = Charset('iso-8859-1')
85 msg.set_charset(charset)
86 eq(msg['mime-version'], '1.0')
87 eq(msg.get_type(), 'text/plain')
88 eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
89 eq(msg.get_param('charset'), 'iso-8859-1')
90 eq(msg['content-transfer-encoding'], 'quoted-printable')
91 eq(msg.get_charset().input_charset, 'iso-8859-1')
92 # Remove the charset
93 msg.set_charset(None)
94 eq(msg.get_charset(), None)
95 eq(msg['content-type'], 'text/plain')
96 # Try adding a charset when there's already MIME headers present
97 msg = Message()
98 msg['MIME-Version'] = '2.0'
99 msg['Content-Type'] = 'text/x-weird'
100 msg['Content-Transfer-Encoding'] = 'quinted-puntable'
101 msg.set_charset(charset)
102 eq(msg['mime-version'], '2.0')
103 eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"')
104 eq(msg['content-transfer-encoding'], 'quinted-puntable')
105
106 def test_set_charset_from_string(self):
107 eq = self.assertEqual
108 msg = Message()
109 msg.set_charset('us-ascii')
110 eq(msg.get_charset().input_charset, 'us-ascii')
111 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
112
113 def test_set_payload_with_charset(self):
114 msg = Message()
115 charset = Charset('iso-8859-1')
116 msg.set_payload('This is a string payload', charset)
117 self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1')
118
Barry Warsaw41075852001-09-23 03:18:13 +0000119 def test_get_charsets(self):
120 eq = self.assertEqual
Tim Peters527e64f2001-10-04 05:36:56 +0000121
Barry Warsaw65279d02001-09-26 05:47:08 +0000122 msg = self._msgobj('msg_08.txt')
123 charsets = msg.get_charsets()
124 eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000125
Barry Warsaw65279d02001-09-26 05:47:08 +0000126 msg = self._msgobj('msg_09.txt')
127 charsets = msg.get_charsets('dingbat')
128 eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
129 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000130
Barry Warsaw65279d02001-09-26 05:47:08 +0000131 msg = self._msgobj('msg_12.txt')
132 charsets = msg.get_charsets()
133 eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
134 'iso-8859-3', 'us-ascii', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000135
136 def test_get_filename(self):
137 eq = self.assertEqual
138
Barry Warsaw65279d02001-09-26 05:47:08 +0000139 msg = self._msgobj('msg_04.txt')
140 filenames = [p.get_filename() for p in msg.get_payload()]
Barry Warsaw41075852001-09-23 03:18:13 +0000141 eq(filenames, ['msg.txt', 'msg.txt'])
142
Barry Warsaw65279d02001-09-26 05:47:08 +0000143 msg = self._msgobj('msg_07.txt')
144 subpart = msg.get_payload(1)
Barry Warsaw41075852001-09-23 03:18:13 +0000145 eq(subpart.get_filename(), 'dingusfish.gif')
146
147 def test_get_boundary(self):
148 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000149 msg = self._msgobj('msg_07.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000150 # No quotes!
Barry Warsaw65279d02001-09-26 05:47:08 +0000151 eq(msg.get_boundary(), 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000152
153 def test_set_boundary(self):
154 eq = self.assertEqual
155 # This one has no existing boundary parameter, but the Content-Type:
156 # header appears fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000157 msg = self._msgobj('msg_01.txt')
158 msg.set_boundary('BOUNDARY')
159 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000160 eq(header.lower(), 'content-type')
161 eq(value, 'text/plain; charset=us-ascii; boundary="BOUNDARY"')
162 # This one has a Content-Type: header, with a boundary, stuck in the
163 # middle of its headers. Make sure the order is preserved; it should
164 # be fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000165 msg = self._msgobj('msg_04.txt')
166 msg.set_boundary('BOUNDARY')
167 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000168 eq(header.lower(), 'content-type')
169 eq(value, 'multipart/mixed; boundary="BOUNDARY"')
170 # And this one has no Content-Type: header at all.
Barry Warsaw65279d02001-09-26 05:47:08 +0000171 msg = self._msgobj('msg_03.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000172 self.assertRaises(Errors.HeaderParseError,
Barry Warsaw65279d02001-09-26 05:47:08 +0000173 msg.set_boundary, 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000174
175 def test_get_decoded_payload(self):
176 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000177 msg = self._msgobj('msg_10.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000178 # The outer message is a multipart
Barry Warsaw65279d02001-09-26 05:47:08 +0000179 eq(msg.get_payload(decode=1), None)
Barry Warsaw41075852001-09-23 03:18:13 +0000180 # Subpart 1 is 7bit encoded
Barry Warsaw65279d02001-09-26 05:47:08 +0000181 eq(msg.get_payload(0).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000182 'This is a 7bit encoded message.\n')
183 # Subpart 2 is quopri
Barry Warsaw65279d02001-09-26 05:47:08 +0000184 eq(msg.get_payload(1).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000185 '\xa1This is a Quoted Printable encoded message!\n')
186 # Subpart 3 is base64
Barry Warsaw65279d02001-09-26 05:47:08 +0000187 eq(msg.get_payload(2).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000188 'This is a Base64 encoded message.')
189 # Subpart 4 has no Content-Transfer-Encoding: header.
Barry Warsaw65279d02001-09-26 05:47:08 +0000190 eq(msg.get_payload(3).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000191 'This has no Content-Transfer-Encoding: header.\n')
192
193 def test_decoded_generator(self):
194 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000195 msg = self._msgobj('msg_07.txt')
196 fp = openfile('msg_17.txt')
197 try:
198 text = fp.read()
199 finally:
200 fp.close()
Barry Warsaw41075852001-09-23 03:18:13 +0000201 s = StringIO()
202 g = DecodedGenerator(s)
Barry Warsaw2c685062002-06-02 19:09:27 +0000203 g.flatten(msg)
Barry Warsaw65279d02001-09-26 05:47:08 +0000204 eq(s.getvalue(), text)
Barry Warsaw41075852001-09-23 03:18:13 +0000205
206 def test__contains__(self):
207 msg = Message()
208 msg['From'] = 'Me'
209 msg['to'] = 'You'
210 # Check for case insensitivity
211 self.failUnless('from' in msg)
212 self.failUnless('From' in msg)
213 self.failUnless('FROM' in msg)
214 self.failUnless('to' in msg)
215 self.failUnless('To' in msg)
216 self.failUnless('TO' in msg)
217
218 def test_as_string(self):
219 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000220 msg = self._msgobj('msg_01.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000221 fp = openfile('msg_01.txt')
222 try:
223 text = fp.read()
224 finally:
225 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000226 eq(text, msg.as_string())
227 fullrepr = str(msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000228 lines = fullrepr.split('\n')
229 self.failUnless(lines[0].startswith('From '))
230 eq(text, NL.join(lines[1:]))
231
232 def test_bad_param(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000233 msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000234 self.assertEqual(msg.get_param('baz'), '')
235
236 def test_missing_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000237 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000238 self.assertEqual(msg.get_filename(), None)
239
240 def test_bogus_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000241 msg = email.message_from_string(
242 "Content-Disposition: blarg; filename\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000243 self.assertEqual(msg.get_filename(), '')
Tim Peters527e64f2001-10-04 05:36:56 +0000244
Barry Warsaw41075852001-09-23 03:18:13 +0000245 def test_missing_boundary(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000246 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000247 self.assertEqual(msg.get_boundary(), None)
248
Barry Warsaw65279d02001-09-26 05:47:08 +0000249 def test_get_params(self):
250 eq = self.assertEqual
251 msg = email.message_from_string(
252 'X-Header: foo=one; bar=two; baz=three\n')
253 eq(msg.get_params(header='x-header'),
254 [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
255 msg = email.message_from_string(
256 'X-Header: foo; bar=one; baz=two\n')
257 eq(msg.get_params(header='x-header'),
258 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
259 eq(msg.get_params(), None)
260 msg = email.message_from_string(
261 'X-Header: foo; bar="one"; baz=two\n')
262 eq(msg.get_params(header='x-header'),
263 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
264
Barry Warsaw409a4c02002-04-10 21:01:31 +0000265 def test_get_param_liberal(self):
266 msg = Message()
267 msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"'
268 self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG')
269
Barry Warsaw65279d02001-09-26 05:47:08 +0000270 def test_get_param(self):
271 eq = self.assertEqual
272 msg = email.message_from_string(
273 "X-Header: foo=one; bar=two; baz=three\n")
274 eq(msg.get_param('bar', header='x-header'), 'two')
275 eq(msg.get_param('quuz', header='x-header'), None)
276 eq(msg.get_param('quuz'), None)
277 msg = email.message_from_string(
278 'X-Header: foo; bar="one"; baz=two\n')
279 eq(msg.get_param('foo', header='x-header'), '')
280 eq(msg.get_param('bar', header='x-header'), 'one')
281 eq(msg.get_param('baz', header='x-header'), 'two')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000282 # XXX: We are not RFC-2045 compliant! We cannot parse:
283 # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"'
284 # msg.get_param("weird")
285 # yet.
Barry Warsaw65279d02001-09-26 05:47:08 +0000286
Barry Warsaw2539cf52001-10-25 22:43:46 +0000287 def test_get_param_funky_continuation_lines(self):
288 msg = self._msgobj('msg_22.txt')
289 self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG')
290
Barry Warsaw65279d02001-09-26 05:47:08 +0000291 def test_has_key(self):
292 msg = email.message_from_string('Header: exists')
293 self.failUnless(msg.has_key('header'))
294 self.failUnless(msg.has_key('Header'))
295 self.failUnless(msg.has_key('HEADER'))
296 self.failIf(msg.has_key('headeri'))
297
Barry Warsaw409a4c02002-04-10 21:01:31 +0000298 def test_set_param(self):
299 eq = self.assertEqual
300 msg = Message()
301 msg.set_param('charset', 'iso-2022-jp')
302 eq(msg.get_param('charset'), 'iso-2022-jp')
303 msg.set_param('importance', 'high value')
304 eq(msg.get_param('importance'), 'high value')
305 eq(msg.get_param('importance', unquote=0), '"high value"')
306 eq(msg.get_params(), [('text/plain', ''),
307 ('charset', 'iso-2022-jp'),
308 ('importance', 'high value')])
309 eq(msg.get_params(unquote=0), [('text/plain', ''),
310 ('charset', '"iso-2022-jp"'),
311 ('importance', '"high value"')])
312 msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy')
313 eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx')
Barry Warsaw41075852001-09-23 03:18:13 +0000314
Barry Warsaw409a4c02002-04-10 21:01:31 +0000315 def test_del_param(self):
316 eq = self.assertEqual
317 msg = self._msgobj('msg_05.txt')
318 eq(msg.get_params(),
319 [('multipart/report', ''), ('report-type', 'delivery-status'),
320 ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
321 old_val = msg.get_param("report-type")
322 msg.del_param("report-type")
323 eq(msg.get_params(),
324 [('multipart/report', ''),
Barry Warsaw16f90552002-04-16 05:06:42 +0000325 ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
Barry Warsaw409a4c02002-04-10 21:01:31 +0000326 msg.set_param("report-type", old_val)
327 eq(msg.get_params(),
328 [('multipart/report', ''),
329 ('boundary', 'D1690A7AC1.996856090/mail.example.com'),
330 ('report-type', old_val)])
331
332 def test_set_type(self):
333 eq = self.assertEqual
334 msg = Message()
335 self.assertRaises(ValueError, msg.set_type, 'text')
336 msg.set_type('text/plain')
337 eq(msg['content-type'], 'text/plain')
338 msg.set_param('charset', 'us-ascii')
339 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
340 msg.set_type('text/html')
341 eq(msg['content-type'], 'text/html; charset="us-ascii"')
342
Barry Warsaw16f90552002-04-16 05:06:42 +0000343
Barry Warsaw08a534d2001-10-04 17:58:50 +0000344
Barry Warsaw41075852001-09-23 03:18:13 +0000345# Test the email.Encoders module
346class TestEncoders(unittest.TestCase):
347 def test_encode_noop(self):
348 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000349 msg = MIMEText('hello world', _encoder=Encoders.encode_noop)
Barry Warsaw41075852001-09-23 03:18:13 +0000350 eq(msg.get_payload(), 'hello world\n')
Barry Warsaw41075852001-09-23 03:18:13 +0000351
352 def test_encode_7bit(self):
353 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000354 msg = MIMEText('hello world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000355 eq(msg.get_payload(), 'hello world\n')
356 eq(msg['content-transfer-encoding'], '7bit')
Barry Warsaw65279d02001-09-26 05:47:08 +0000357 msg = MIMEText('hello \x7f world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000358 eq(msg.get_payload(), 'hello \x7f world\n')
359 eq(msg['content-transfer-encoding'], '7bit')
360
361 def test_encode_8bit(self):
362 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000363 msg = MIMEText('hello \x80 world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000364 eq(msg.get_payload(), 'hello \x80 world\n')
365 eq(msg['content-transfer-encoding'], '8bit')
366
Barry Warsaw409a4c02002-04-10 21:01:31 +0000367 def test_encode_empty_payload(self):
368 eq = self.assertEqual
369 msg = Message()
370 msg.set_charset('us-ascii')
371 eq(msg['content-transfer-encoding'], '7bit')
372
Barry Warsaw41075852001-09-23 03:18:13 +0000373 def test_encode_base64(self):
374 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000375 msg = MIMEText('hello world', _encoder=Encoders.encode_base64)
Barry Warsaw41075852001-09-23 03:18:13 +0000376 eq(msg.get_payload(), 'aGVsbG8gd29ybGQK\n')
377 eq(msg['content-transfer-encoding'], 'base64')
378
379 def test_encode_quoted_printable(self):
380 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000381 msg = MIMEText('hello world', _encoder=Encoders.encode_quopri)
Barry Warsaw41075852001-09-23 03:18:13 +0000382 eq(msg.get_payload(), 'hello=20world\n')
383 eq(msg['content-transfer-encoding'], 'quoted-printable')
384
Barry Warsaw409a4c02002-04-10 21:01:31 +0000385 def test_default_cte(self):
386 eq = self.assertEqual
387 msg = MIMEText('hello world')
388 eq(msg['content-transfer-encoding'], '7bit')
389
390 def test_default_cte(self):
391 eq = self.assertEqual
392 # With no explicit _charset its us-ascii, and all are 7-bit
393 msg = MIMEText('hello world')
394 eq(msg['content-transfer-encoding'], '7bit')
395 # Similar, but with 8-bit data
396 msg = MIMEText('hello \xf8 world')
397 eq(msg['content-transfer-encoding'], '8bit')
398 # And now with a different charset
399 msg = MIMEText('hello \xf8 world', _charset='iso-8859-1')
400 eq(msg['content-transfer-encoding'], 'quoted-printable')
401
Barry Warsaw41075852001-09-23 03:18:13 +0000402
Barry Warsaw08a534d2001-10-04 17:58:50 +0000403
404# Test long header wrapping
Barry Warsawb6a92132002-06-28 23:49:33 +0000405class TestLongHeaders(TestEmailBase):
406 def test_split_long_continuation(self):
407 eq = self.ndiffAssertEqual
408 msg = email.message_from_string("""\
409Subject: bug demonstration
410\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
411\tmore text
412
413test
414""")
415 sfp = StringIO()
416 g = Generator(sfp)
417 g.flatten(msg)
418 eq(sfp.getvalue(), """\
419Subject: bug demonstration
420\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
421\tmore text
422
423test
424""")
425
426 def test_another_long_almost_unsplittable_header(self):
427 eq = self.ndiffAssertEqual
428 hstr = """\
429bug demonstration
430\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
431\tmore text"""
432 h = Header(hstr, continuation_ws='\t')
433 eq(h.encode(), """\
434bug demonstration
435\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
436\tmore text""")
437 h = Header(hstr)
438 eq(h.encode(), """\
439bug demonstration
440 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
441 more text""")
442
443 def test_long_nonstring(self):
444 eq = self.ndiffAssertEqual
445 g = Charset("iso-8859-1")
446 cz = Charset("iso-8859-2")
447 utf8 = Charset("utf-8")
448 g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
449 cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
450 utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
451 h = Header(g_head, g)
452 h.append(cz_head, cz)
453 h.append(utf8_head, utf8)
454 msg = Message()
455 msg['Subject'] = h
456 sfp = StringIO()
457 g = Generator(sfp)
458 g.flatten(msg)
459 eq(sfp.getvalue(), '''\
460Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
461 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
462 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
463 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
464 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
465 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
466 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
467 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
468 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
469 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
470 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
471 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
472 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
473 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
474 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=
475
476''')
477 eq(h.encode(), '''\
478=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
479 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
480 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
481 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
482 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
483 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
484 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
485 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
486 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
487 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
488 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
489 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
490 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
491 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
492 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=''')
493
494 def test_long_header_encode(self):
495 eq = self.ndiffAssertEqual
496 h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
497 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
498 header_name='X-Foobar-Spoink-Defrobnit')
499 eq(h.encode(), '''\
500wasnipoop; giraffes="very-long-necked-animals";
501 spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
502
503 def test_long_header_encode_with_tab_continuation(self):
504 eq = self.ndiffAssertEqual
505 h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
506 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
507 header_name='X-Foobar-Spoink-Defrobnit',
508 continuation_ws='\t')
509 eq(h.encode(), '''\
510wasnipoop; giraffes="very-long-necked-animals";
511\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
512
Barry Warsaw41075852001-09-23 03:18:13 +0000513 def test_header_splitter(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000514 eq = self.ndiffAssertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000515 msg = MIMEText('')
Barry Warsaw41075852001-09-23 03:18:13 +0000516 # It'd be great if we could use add_header() here, but that doesn't
517 # guarantee an order of the parameters.
518 msg['X-Foobar-Spoink-Defrobnit'] = (
519 'wasnipoop; giraffes="very-long-necked-animals"; '
520 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
521 sfp = StringIO()
522 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +0000523 g.flatten(msg)
Barry Warsawb6a92132002-06-28 23:49:33 +0000524 eq(sfp.getvalue(), '''\
Barry Warsaw409a4c02002-04-10 21:01:31 +0000525Content-Type: text/plain; charset="us-ascii"
526MIME-Version: 1.0
527Content-Transfer-Encoding: 7bit
528X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
Barry Warsaw16f90552002-04-16 05:06:42 +0000529\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"
Barry Warsaw409a4c02002-04-10 21:01:31 +0000530
531''')
Barry Warsaw41075852001-09-23 03:18:13 +0000532
Barry Warsaw07227d12001-10-17 20:52:26 +0000533 def test_no_semis_header_splitter(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000534 eq = self.ndiffAssertEqual
Barry Warsaw07227d12001-10-17 20:52:26 +0000535 msg = Message()
536 msg['From'] = 'test@dom.ain'
Barry Warsawb6a92132002-06-28 23:49:33 +0000537 msg['References'] = SPACE.join(['<%d@dom.ain>' % i for i in range(10)])
Barry Warsaw07227d12001-10-17 20:52:26 +0000538 msg.set_payload('Test')
539 sfp = StringIO()
540 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +0000541 g.flatten(msg)
Barry Warsawb6a92132002-06-28 23:49:33 +0000542 eq(sfp.getvalue(), """\
Barry Warsaw07227d12001-10-17 20:52:26 +0000543From: test@dom.ain
544References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
Tim Peterse0c446b2001-10-18 21:57:37 +0000545\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
Barry Warsaw07227d12001-10-17 20:52:26 +0000546
547Test""")
548
549 def test_no_split_long_header(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000550 eq = self.ndiffAssertEqual
551 hstr = 'References: ' + 'x' * 80
552 h = Header(hstr, continuation_ws='\t')
553 eq(h.encode(), """\
554References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""")
Barry Warsaw07227d12001-10-17 20:52:26 +0000555
Barry Warsaw409a4c02002-04-10 21:01:31 +0000556 def test_splitting_multiple_long_lines(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000557 eq = self.ndiffAssertEqual
558 hstr = """\
Barry Warsaw409a4c02002-04-10 21:01:31 +0000559from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
Barry Warsaw16f90552002-04-16 05:06:42 +0000560\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
561\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
Barry Warsaw409a4c02002-04-10 21:01:31 +0000562"""
Barry Warsawb6a92132002-06-28 23:49:33 +0000563 h = Header(hstr, continuation_ws='\t')
564 eq(h.encode(), """\
565from babylon.socal-raves.org (localhost [127.0.0.1]);
Barry Warsaw16f90552002-04-16 05:06:42 +0000566\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
567\tfor <mailman-admin@babylon.socal-raves.org>;
568\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
569\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
570\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
571\tfor <mailman-admin@babylon.socal-raves.org>;
572\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
573\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
574\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
575\tfor <mailman-admin@babylon.socal-raves.org>;
Barry Warsawb6a92132002-06-28 23:49:33 +0000576\tSat, 2 Feb 2002 17:00:06 -0800 (PST)""")
Barry Warsaw409a4c02002-04-10 21:01:31 +0000577
Barry Warsaw41075852001-09-23 03:18:13 +0000578
Barry Warsaw08a534d2001-10-04 17:58:50 +0000579
580# Test mangling of "From " lines in the body of a message
Barry Warsaw41075852001-09-23 03:18:13 +0000581class TestFromMangling(unittest.TestCase):
582 def setUp(self):
583 self.msg = Message()
584 self.msg['From'] = 'aaa@bbb.org'
Barry Warsaw2c685062002-06-02 19:09:27 +0000585 self.msg.set_payload("""\
Barry Warsaw41075852001-09-23 03:18:13 +0000586From the desk of A.A.A.:
587Blah blah blah
588""")
589
590 def test_mangled_from(self):
591 s = StringIO()
592 g = Generator(s, mangle_from_=1)
Barry Warsaw2c685062002-06-02 19:09:27 +0000593 g.flatten(self.msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000594 self.assertEqual(s.getvalue(), """\
595From: aaa@bbb.org
596
597>From the desk of A.A.A.:
598Blah blah blah
599""")
600
601 def test_dont_mangle_from(self):
602 s = StringIO()
603 g = Generator(s, mangle_from_=0)
Barry Warsaw2c685062002-06-02 19:09:27 +0000604 g.flatten(self.msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000605 self.assertEqual(s.getvalue(), """\
606From: aaa@bbb.org
607
608From the desk of A.A.A.:
609Blah blah blah
610""")
611
612
Barry Warsaw08a534d2001-10-04 17:58:50 +0000613
Barry Warsawfee435a2001-10-09 19:23:57 +0000614# Test the basic MIMEAudio class
615class TestMIMEAudio(unittest.TestCase):
616 def setUp(self):
617 # In Python, audiotest.au lives in Lib/test not Lib/test/data
618 fp = open(findfile('audiotest.au'))
619 try:
620 self._audiodata = fp.read()
621 finally:
622 fp.close()
623 self._au = MIMEAudio(self._audiodata)
624
625 def test_guess_minor_type(self):
626 self.assertEqual(self._au.get_type(), 'audio/basic')
627
628 def test_encoding(self):
629 payload = self._au.get_payload()
630 self.assertEqual(base64.decodestring(payload), self._audiodata)
631
632 def checkSetMinor(self):
633 au = MIMEAudio(self._audiodata, 'fish')
634 self.assertEqual(im.get_type(), 'audio/fish')
635
636 def test_custom_encoder(self):
637 eq = self.assertEqual
638 def encoder(msg):
639 orig = msg.get_payload()
640 msg.set_payload(0)
641 msg['Content-Transfer-Encoding'] = 'broken64'
642 au = MIMEAudio(self._audiodata, _encoder=encoder)
643 eq(au.get_payload(), 0)
644 eq(au['content-transfer-encoding'], 'broken64')
645
646 def test_add_header(self):
647 eq = self.assertEqual
648 unless = self.failUnless
649 self._au.add_header('Content-Disposition', 'attachment',
650 filename='audiotest.au')
651 eq(self._au['content-disposition'],
652 'attachment; filename="audiotest.au"')
653 eq(self._au.get_params(header='content-disposition'),
654 [('attachment', ''), ('filename', 'audiotest.au')])
655 eq(self._au.get_param('filename', header='content-disposition'),
656 'audiotest.au')
657 missing = []
658 eq(self._au.get_param('attachment', header='content-disposition'), '')
659 unless(self._au.get_param('foo', failobj=missing,
660 header='content-disposition') is missing)
661 # Try some missing stuff
662 unless(self._au.get_param('foobar', missing) is missing)
663 unless(self._au.get_param('attachment', missing,
664 header='foobar') is missing)
665
666
667
Barry Warsaw65279d02001-09-26 05:47:08 +0000668# Test the basic MIMEImage class
669class TestMIMEImage(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000670 def setUp(self):
671 fp = openfile('PyBanner048.gif')
672 try:
673 self._imgdata = fp.read()
674 finally:
675 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000676 self._im = MIMEImage(self._imgdata)
Barry Warsaw41075852001-09-23 03:18:13 +0000677
678 def test_guess_minor_type(self):
679 self.assertEqual(self._im.get_type(), 'image/gif')
680
681 def test_encoding(self):
682 payload = self._im.get_payload()
683 self.assertEqual(base64.decodestring(payload), self._imgdata)
684
685 def checkSetMinor(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000686 im = MIMEImage(self._imgdata, 'fish')
Barry Warsaw41075852001-09-23 03:18:13 +0000687 self.assertEqual(im.get_type(), 'image/fish')
688
689 def test_custom_encoder(self):
690 eq = self.assertEqual
691 def encoder(msg):
692 orig = msg.get_payload()
693 msg.set_payload(0)
694 msg['Content-Transfer-Encoding'] = 'broken64'
Barry Warsaw65279d02001-09-26 05:47:08 +0000695 im = MIMEImage(self._imgdata, _encoder=encoder)
Barry Warsaw41075852001-09-23 03:18:13 +0000696 eq(im.get_payload(), 0)
697 eq(im['content-transfer-encoding'], 'broken64')
698
699 def test_add_header(self):
700 eq = self.assertEqual
701 unless = self.failUnless
702 self._im.add_header('Content-Disposition', 'attachment',
703 filename='dingusfish.gif')
704 eq(self._im['content-disposition'],
705 'attachment; filename="dingusfish.gif"')
706 eq(self._im.get_params(header='content-disposition'),
Barry Warsaw65279d02001-09-26 05:47:08 +0000707 [('attachment', ''), ('filename', 'dingusfish.gif')])
Barry Warsaw41075852001-09-23 03:18:13 +0000708 eq(self._im.get_param('filename', header='content-disposition'),
709 'dingusfish.gif')
710 missing = []
Barry Warsaw65279d02001-09-26 05:47:08 +0000711 eq(self._im.get_param('attachment', header='content-disposition'), '')
712 unless(self._im.get_param('foo', failobj=missing,
Barry Warsaw41075852001-09-23 03:18:13 +0000713 header='content-disposition') is missing)
714 # Try some missing stuff
715 unless(self._im.get_param('foobar', missing) is missing)
716 unless(self._im.get_param('attachment', missing,
717 header='foobar') is missing)
718
719
Barry Warsaw08a534d2001-10-04 17:58:50 +0000720
Barry Warsaw65279d02001-09-26 05:47:08 +0000721# Test the basic MIMEText class
722class TestMIMEText(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000723 def setUp(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000724 self._msg = MIMEText('hello there')
Barry Warsaw41075852001-09-23 03:18:13 +0000725
726 def test_types(self):
727 eq = self.assertEqual
728 unless = self.failUnless
729 eq(self._msg.get_type(), 'text/plain')
730 eq(self._msg.get_param('charset'), 'us-ascii')
731 missing = []
732 unless(self._msg.get_param('foobar', missing) is missing)
733 unless(self._msg.get_param('charset', missing, header='foobar')
734 is missing)
735
736 def test_payload(self):
737 self.assertEqual(self._msg.get_payload(), 'hello there\n')
738 self.failUnless(not self._msg.is_multipart())
739
Barry Warsaw409a4c02002-04-10 21:01:31 +0000740 def test_charset(self):
741 eq = self.assertEqual
742 msg = MIMEText('hello there', _charset='us-ascii')
743 eq(msg.get_charset().input_charset, 'us-ascii')
744 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
745
Barry Warsaw41075852001-09-23 03:18:13 +0000746
Barry Warsaw08a534d2001-10-04 17:58:50 +0000747
748# Test a more complicated multipart/mixed type message
Barry Warsaw41075852001-09-23 03:18:13 +0000749class TestMultipartMixed(unittest.TestCase):
750 def setUp(self):
751 fp = openfile('PyBanner048.gif')
752 try:
753 data = fp.read()
754 finally:
755 fp.close()
756
757 container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
Barry Warsaw65279d02001-09-26 05:47:08 +0000758 image = MIMEImage(data, name='dingusfish.gif')
Barry Warsaw41075852001-09-23 03:18:13 +0000759 image.add_header('content-disposition', 'attachment',
760 filename='dingusfish.gif')
Barry Warsaw65279d02001-09-26 05:47:08 +0000761 intro = MIMEText('''\
Barry Warsaw41075852001-09-23 03:18:13 +0000762Hi there,
763
764This is the dingus fish.
765''')
Barry Warsaw2c685062002-06-02 19:09:27 +0000766 container.attach(intro)
767 container.attach(image)
Barry Warsaw41075852001-09-23 03:18:13 +0000768 container['From'] = 'Barry <barry@digicool.com>'
769 container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
770 container['Subject'] = 'Here is your dingus fish'
Tim Peters527e64f2001-10-04 05:36:56 +0000771
Barry Warsaw41075852001-09-23 03:18:13 +0000772 now = 987809702.54848599
773 timetuple = time.localtime(now)
774 if timetuple[-1] == 0:
775 tzsecs = time.timezone
776 else:
777 tzsecs = time.altzone
778 if tzsecs > 0:
779 sign = '-'
780 else:
781 sign = '+'
782 tzoffset = ' %s%04d' % (sign, tzsecs / 36)
783 container['Date'] = time.strftime(
784 '%a, %d %b %Y %H:%M:%S',
785 time.localtime(now)) + tzoffset
786 self._msg = container
787 self._im = image
788 self._txt = intro
789
790 def test_hierarchy(self):
791 # convenience
792 eq = self.assertEqual
793 unless = self.failUnless
794 raises = self.assertRaises
795 # tests
796 m = self._msg
797 unless(m.is_multipart())
798 eq(m.get_type(), 'multipart/mixed')
799 eq(len(m.get_payload()), 2)
800 raises(IndexError, m.get_payload, 2)
801 m0 = m.get_payload(0)
802 m1 = m.get_payload(1)
803 unless(m0 is self._txt)
804 unless(m1 is self._im)
805 eq(m.get_payload(), [m0, m1])
806 unless(not m0.is_multipart())
807 unless(not m1.is_multipart())
808
Barry Warsaw409a4c02002-04-10 21:01:31 +0000809 def test_no_parts_in_a_multipart(self):
810 outer = MIMEBase('multipart', 'mixed')
811 outer['Subject'] = 'A subject'
812 outer['To'] = 'aperson@dom.ain'
813 outer['From'] = 'bperson@dom.ain'
814 outer.preamble = ''
815 outer.epilogue = ''
816 outer.set_boundary('BOUNDARY')
817 msg = MIMEText('hello world')
818 self.assertEqual(outer.as_string(), '''\
819Content-Type: multipart/mixed; boundary="BOUNDARY"
820MIME-Version: 1.0
821Subject: A subject
822To: aperson@dom.ain
823From: bperson@dom.ain
824
825--BOUNDARY
826
827
828--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000829''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000830
831 def test_one_part_in_a_multipart(self):
832 outer = MIMEBase('multipart', 'mixed')
833 outer['Subject'] = 'A subject'
834 outer['To'] = 'aperson@dom.ain'
835 outer['From'] = 'bperson@dom.ain'
836 outer.preamble = ''
837 outer.epilogue = ''
838 outer.set_boundary('BOUNDARY')
839 msg = MIMEText('hello world')
840 outer.attach(msg)
841 self.assertEqual(outer.as_string(), '''\
842Content-Type: multipart/mixed; boundary="BOUNDARY"
843MIME-Version: 1.0
844Subject: A subject
845To: aperson@dom.ain
846From: bperson@dom.ain
847
848--BOUNDARY
849Content-Type: text/plain; charset="us-ascii"
850MIME-Version: 1.0
851Content-Transfer-Encoding: 7bit
852
853hello world
854
855--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000856''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000857
858 def test_seq_parts_in_a_multipart(self):
859 outer = MIMEBase('multipart', 'mixed')
860 outer['Subject'] = 'A subject'
861 outer['To'] = 'aperson@dom.ain'
862 outer['From'] = 'bperson@dom.ain'
863 outer.preamble = ''
864 outer.epilogue = ''
865 msg = MIMEText('hello world')
866 outer.attach(msg)
867 outer.set_boundary('BOUNDARY')
868 self.assertEqual(outer.as_string(), '''\
869Content-Type: multipart/mixed; boundary="BOUNDARY"
870MIME-Version: 1.0
871Subject: A subject
872To: aperson@dom.ain
873From: bperson@dom.ain
874
875--BOUNDARY
876Content-Type: text/plain; charset="us-ascii"
877MIME-Version: 1.0
878Content-Transfer-Encoding: 7bit
879
880hello world
881
882--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000883''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000884
Barry Warsaw41075852001-09-23 03:18:13 +0000885
Barry Warsaw08a534d2001-10-04 17:58:50 +0000886
887# Test some badly formatted messages
Barry Warsaw41075852001-09-23 03:18:13 +0000888class TestNonConformant(TestEmailBase):
889 def test_parse_missing_minor_type(self):
890 eq = self.assertEqual
891 msg = self._msgobj('msg_14.txt')
892 eq(msg.get_type(), 'text')
893 eq(msg.get_main_type(), 'text')
894 self.failUnless(msg.get_subtype() is None)
895
896 def test_bogus_boundary(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +0000897 fp = openfile(findfile('msg_15.txt'))
Barry Warsaw41075852001-09-23 03:18:13 +0000898 try:
899 data = fp.read()
900 finally:
901 fp.close()
902 p = Parser()
903 # Note, under a future non-strict parsing mode, this would parse the
904 # message into the intended message tree.
905 self.assertRaises(Errors.BoundaryError, p.parsestr, data)
906
Barry Warsaw409a4c02002-04-10 21:01:31 +0000907 def test_multipart_no_boundary(self):
908 fp = openfile(findfile('msg_25.txt'))
909 self.assertRaises(Errors.BoundaryError, email.message_from_file, fp)
910
Barry Warsaw41075852001-09-23 03:18:13 +0000911
Barry Warsaw08a534d2001-10-04 17:58:50 +0000912
913# Test RFC 2047 header encoding and decoding
Barry Warsaw41075852001-09-23 03:18:13 +0000914class TestRFC2047(unittest.TestCase):
915 def test_iso_8859_1(self):
916 eq = self.assertEqual
917 s = '=?iso-8859-1?q?this=20is=20some=20text?='
918 eq(Utils.decode(s), 'this is some text')
919 s = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?='
Barry Warsaw409a4c02002-04-10 21:01:31 +0000920 eq(Utils.decode(s), u'Keld J\xf8rn Simonsen')
Barry Warsaw41075852001-09-23 03:18:13 +0000921 s = '=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=' \
922 '=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
923 eq(Utils.decode(s), 'If you can read this you understand the example.')
924 s = '=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?='
925 eq(Utils.decode(s),
926 u'\u05dd\u05d5\u05dc\u05e9 \u05df\u05d1 \u05d9\u05dc\u05d8\u05e4\u05e0')
927 s = '=?iso-8859-1?q?this=20is?= =?iso-8859-1?q?some=20text?='
Barry Warsaw409a4c02002-04-10 21:01:31 +0000928 eq(Utils.decode(s), u'this issome text')
929 s = '=?iso-8859-1?q?this=20is_?= =?iso-8859-1?q?some=20text?='
Barry Warsaw41075852001-09-23 03:18:13 +0000930 eq(Utils.decode(s), u'this is some text')
931
932 def test_encode_header(self):
933 eq = self.assertEqual
934 s = 'this is some text'
935 eq(Utils.encode(s), '=?iso-8859-1?q?this=20is=20some=20text?=')
936 s = 'Keld_J\xf8rn_Simonsen'
937 eq(Utils.encode(s), '=?iso-8859-1?q?Keld_J=F8rn_Simonsen?=')
938 s1 = 'If you can read this yo'
939 s2 = 'u understand the example.'
940 eq(Utils.encode(s1, encoding='b'),
941 '=?iso-8859-1?b?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=')
942 eq(Utils.encode(s2, charset='iso-8859-2', encoding='b'),
943 '=?iso-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=')
944
945
Barry Warsaw08a534d2001-10-04 17:58:50 +0000946
947# Test the MIMEMessage class
Barry Warsaw65279d02001-09-26 05:47:08 +0000948class TestMIMEMessage(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +0000949 def setUp(self):
950 fp = openfile('msg_11.txt')
951 self._text = fp.read()
952 fp.close()
953
954 def test_type_error(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000955 self.assertRaises(TypeError, MIMEMessage, 'a plain string')
Barry Warsaw41075852001-09-23 03:18:13 +0000956
957 def test_valid_argument(self):
958 eq = self.assertEqual
Barry Warsaw2c685062002-06-02 19:09:27 +0000959 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +0000960 subject = 'A sub-message'
961 m = Message()
962 m['Subject'] = subject
Barry Warsaw65279d02001-09-26 05:47:08 +0000963 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000964 eq(r.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +0000965 payload = r.get_payload()
966 unless(type(payload), ListType)
967 eq(len(payload), 1)
968 subpart = payload[0]
969 unless(subpart is m)
970 eq(subpart['subject'], subject)
971
972 def test_bad_multipart(self):
973 eq = self.assertEqual
974 msg1 = Message()
975 msg1['Subject'] = 'subpart 1'
976 msg2 = Message()
977 msg2['Subject'] = 'subpart 2'
978 r = MIMEMessage(msg1)
979 self.assertRaises(Errors.MultipartConversionError, r.attach, msg2)
Barry Warsaw41075852001-09-23 03:18:13 +0000980
981 def test_generate(self):
982 # First craft the message to be encapsulated
983 m = Message()
984 m['Subject'] = 'An enclosed message'
Barry Warsaw2c685062002-06-02 19:09:27 +0000985 m.set_payload('Here is the body of the message.\n')
Barry Warsaw65279d02001-09-26 05:47:08 +0000986 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000987 r['Subject'] = 'The enclosing message'
988 s = StringIO()
989 g = Generator(s)
Barry Warsaw2c685062002-06-02 19:09:27 +0000990 g.flatten(r)
Barry Warsaw65279d02001-09-26 05:47:08 +0000991 self.assertEqual(s.getvalue(), """\
992Content-Type: message/rfc822
993MIME-Version: 1.0
994Subject: The enclosing message
995
996Subject: An enclosed message
997
998Here is the body of the message.
999""")
1000
1001 def test_parse_message_rfc822(self):
1002 eq = self.assertEqual
Barry Warsaw2c685062002-06-02 19:09:27 +00001003 unless = self.failUnless
Barry Warsaw65279d02001-09-26 05:47:08 +00001004 msg = self._msgobj('msg_11.txt')
1005 eq(msg.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001006 payload = msg.get_payload()
1007 unless(isinstance(payload, ListType))
1008 eq(len(payload), 1)
1009 submsg = payload[0]
Barry Warsaw65279d02001-09-26 05:47:08 +00001010 self.failUnless(isinstance(submsg, Message))
1011 eq(submsg['subject'], 'An enclosed message')
1012 eq(submsg.get_payload(), 'Here is the body of the message.\n')
1013
1014 def test_dsn(self):
1015 eq = self.assertEqual
1016 unless = self.failUnless
1017 # msg 16 is a Delivery Status Notification, see RFC XXXX
1018 msg = self._msgobj('msg_16.txt')
1019 eq(msg.get_type(), 'multipart/report')
1020 unless(msg.is_multipart())
1021 eq(len(msg.get_payload()), 3)
1022 # Subpart 1 is a text/plain, human readable section
1023 subpart = msg.get_payload(0)
1024 eq(subpart.get_type(), 'text/plain')
1025 eq(subpart.get_payload(), """\
1026This report relates to a message you sent with the following header fields:
1027
1028 Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
1029 Date: Sun, 23 Sep 2001 20:10:55 -0700
1030 From: "Ian T. Henry" <henryi@oxy.edu>
1031 To: SoCal Raves <scr@socal-raves.org>
1032 Subject: [scr] yeah for Ians!!
1033
1034Your message cannot be delivered to the following recipients:
1035
1036 Recipient address: jangel1@cougar.noc.ucla.edu
1037 Reason: recipient reached disk quota
1038
1039""")
1040 # Subpart 2 contains the machine parsable DSN information. It
1041 # consists of two blocks of headers, represented by two nested Message
1042 # objects.
1043 subpart = msg.get_payload(1)
1044 eq(subpart.get_type(), 'message/delivery-status')
1045 eq(len(subpart.get_payload()), 2)
1046 # message/delivery-status should treat each block as a bunch of
1047 # headers, i.e. a bunch of Message objects.
1048 dsn1 = subpart.get_payload(0)
1049 unless(isinstance(dsn1, Message))
1050 eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu')
1051 eq(dsn1.get_param('dns', header='reporting-mta'), '')
1052 # Try a missing one <wink>
1053 eq(dsn1.get_param('nsd', header='reporting-mta'), None)
1054 dsn2 = subpart.get_payload(1)
1055 unless(isinstance(dsn2, Message))
1056 eq(dsn2['action'], 'failed')
1057 eq(dsn2.get_params(header='original-recipient'),
1058 [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')])
1059 eq(dsn2.get_param('rfc822', header='final-recipient'), '')
1060 # Subpart 3 is the original message
1061 subpart = msg.get_payload(2)
1062 eq(subpart.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001063 payload = subpart.get_payload()
1064 unless(isinstance(payload, ListType))
1065 eq(len(payload), 1)
1066 subsubpart = payload[0]
Barry Warsaw65279d02001-09-26 05:47:08 +00001067 unless(isinstance(subsubpart, Message))
1068 eq(subsubpart.get_type(), 'text/plain')
1069 eq(subsubpart['message-id'],
1070 '<002001c144a6$8752e060$56104586@oxy.edu>')
Barry Warsaw41075852001-09-23 03:18:13 +00001071
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001072 def test_epilogue(self):
1073 fp = openfile('msg_21.txt')
1074 try:
1075 text = fp.read()
1076 finally:
1077 fp.close()
1078 msg = Message()
1079 msg['From'] = 'aperson@dom.ain'
1080 msg['To'] = 'bperson@dom.ain'
1081 msg['Subject'] = 'Test'
1082 msg.preamble = 'MIME message\n'
1083 msg.epilogue = 'End of MIME message\n'
1084 msg1 = MIMEText('One')
1085 msg2 = MIMEText('Two')
1086 msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
Barry Warsaw2c685062002-06-02 19:09:27 +00001087 msg.attach(msg1)
1088 msg.attach(msg2)
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001089 sfp = StringIO()
1090 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +00001091 g.flatten(msg)
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001092 self.assertEqual(sfp.getvalue(), text)
1093
Barry Warsaw41075852001-09-23 03:18:13 +00001094
Barry Warsaw08a534d2001-10-04 17:58:50 +00001095
1096# A general test of parser->model->generator idempotency. IOW, read a message
1097# in, parse it into a message object tree, then without touching the tree,
1098# regenerate the plain text. The original text and the transformed text
1099# should be identical. Note: that we ignore the Unix-From since that may
1100# contain a changed date.
Barry Warsawb6a92132002-06-28 23:49:33 +00001101class TestIdempotent(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +00001102 def _msgobj(self, filename):
1103 fp = openfile(filename)
1104 try:
1105 data = fp.read()
1106 finally:
1107 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +00001108 msg = email.message_from_string(data)
1109 return msg, data
Barry Warsaw41075852001-09-23 03:18:13 +00001110
1111 def _idempotent(self, msg, text):
Barry Warsawb6a92132002-06-28 23:49:33 +00001112 eq = self.ndiffAssertEqual
Barry Warsaw41075852001-09-23 03:18:13 +00001113 s = StringIO()
1114 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001115 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001116 eq(text, s.getvalue())
1117
1118 def test_parse_text_message(self):
1119 eq = self.assertEquals
1120 msg, text = self._msgobj('msg_01.txt')
1121 eq(msg.get_type(), 'text/plain')
1122 eq(msg.get_main_type(), 'text')
1123 eq(msg.get_subtype(), 'plain')
Barry Warsaw65279d02001-09-26 05:47:08 +00001124 eq(msg.get_params()[1], ('charset', 'us-ascii'))
Barry Warsaw41075852001-09-23 03:18:13 +00001125 eq(msg.get_param('charset'), 'us-ascii')
1126 eq(msg.preamble, None)
1127 eq(msg.epilogue, None)
1128 self._idempotent(msg, text)
1129
1130 def test_parse_untyped_message(self):
1131 eq = self.assertEquals
1132 msg, text = self._msgobj('msg_03.txt')
1133 eq(msg.get_type(), None)
1134 eq(msg.get_params(), None)
1135 eq(msg.get_param('charset'), None)
1136 self._idempotent(msg, text)
1137
1138 def test_simple_multipart(self):
1139 msg, text = self._msgobj('msg_04.txt')
1140 self._idempotent(msg, text)
1141
1142 def test_MIME_digest(self):
1143 msg, text = self._msgobj('msg_02.txt')
1144 self._idempotent(msg, text)
1145
Barry Warsawb6a92132002-06-28 23:49:33 +00001146## def test_MIME_digest_with_part_headers(self):
1147## msg, text = self._msgobj('msg_28.txt')
1148## self._idempotent(msg, text)
1149
Barry Warsaw41075852001-09-23 03:18:13 +00001150 def test_mixed_with_image(self):
1151 msg, text = self._msgobj('msg_06.txt')
1152 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +00001153
Barry Warsaw41075852001-09-23 03:18:13 +00001154 def test_multipart_report(self):
1155 msg, text = self._msgobj('msg_05.txt')
1156 self._idempotent(msg, text)
Barry Warsaw65279d02001-09-26 05:47:08 +00001157
1158 def test_dsn(self):
1159 msg, text = self._msgobj('msg_16.txt')
1160 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +00001161
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001162 def test_preamble_epilogue(self):
1163 msg, text = self._msgobj('msg_21.txt')
1164 self._idempotent(msg, text)
1165
Barry Warsaw763af412002-01-27 06:48:47 +00001166 def test_multipart_one_part(self):
1167 msg, text = self._msgobj('msg_23.txt')
1168 self._idempotent(msg, text)
1169
Barry Warsaw409a4c02002-04-10 21:01:31 +00001170 def test_multipart_no_parts(self):
1171 msg, text = self._msgobj('msg_24.txt')
1172 self._idempotent(msg, text)
1173
Barry Warsaw41075852001-09-23 03:18:13 +00001174 def test_content_type(self):
1175 eq = self.assertEquals
Barry Warsaw2c685062002-06-02 19:09:27 +00001176 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +00001177 # Get a message object and reset the seek pointer for other tests
1178 msg, text = self._msgobj('msg_05.txt')
1179 eq(msg.get_type(), 'multipart/report')
1180 # Test the Content-Type: parameters
1181 params = {}
Barry Warsaw65279d02001-09-26 05:47:08 +00001182 for pk, pv in msg.get_params():
Barry Warsaw41075852001-09-23 03:18:13 +00001183 params[pk] = pv
1184 eq(params['report-type'], 'delivery-status')
Barry Warsaw65279d02001-09-26 05:47:08 +00001185 eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
Barry Warsaw41075852001-09-23 03:18:13 +00001186 eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
1187 eq(msg.epilogue, '\n\n')
1188 eq(len(msg.get_payload()), 3)
1189 # Make sure the subparts are what we expect
1190 msg1 = msg.get_payload(0)
1191 eq(msg1.get_type(), 'text/plain')
1192 eq(msg1.get_payload(), 'Yadda yadda yadda\n')
1193 msg2 = msg.get_payload(1)
1194 eq(msg2.get_type(), None)
1195 eq(msg2.get_payload(), 'Yadda yadda yadda\n')
1196 msg3 = msg.get_payload(2)
1197 eq(msg3.get_type(), 'message/rfc822')
1198 self.failUnless(isinstance(msg3, Message))
Barry Warsaw2c685062002-06-02 19:09:27 +00001199 payload = msg3.get_payload()
1200 unless(isinstance(payload, ListType))
1201 eq(len(payload), 1)
1202 msg4 = payload[0]
1203 unless(isinstance(msg4, Message))
Barry Warsaw41075852001-09-23 03:18:13 +00001204 eq(msg4.get_payload(), 'Yadda yadda yadda\n')
1205
1206 def test_parser(self):
1207 eq = self.assertEquals
Barry Warsaw2c685062002-06-02 19:09:27 +00001208 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +00001209 msg, text = self._msgobj('msg_06.txt')
1210 # Check some of the outer headers
1211 eq(msg.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001212 # Make sure the payload is a list of exactly one sub-Message, and that
1213 # that submessage has a type of text/plain
1214 payload = msg.get_payload()
1215 unless(isinstance(payload, ListType))
1216 eq(len(payload), 1)
1217 msg1 = payload[0]
Barry Warsaw41075852001-09-23 03:18:13 +00001218 self.failUnless(isinstance(msg1, Message))
1219 eq(msg1.get_type(), 'text/plain')
1220 self.failUnless(isinstance(msg1.get_payload(), StringType))
1221 eq(msg1.get_payload(), '\n')
Barry Warsaw41075852001-09-23 03:18:13 +00001222
Barry Warsaw08a534d2001-10-04 17:58:50 +00001223
1224# Test various other bits of the package's functionality
Barry Warsaw41075852001-09-23 03:18:13 +00001225class TestMiscellaneous(unittest.TestCase):
1226 def test_message_from_string(self):
1227 fp = openfile('msg_01.txt')
1228 try:
1229 text = fp.read()
1230 finally:
1231 fp.close()
1232 msg = email.message_from_string(text)
1233 s = StringIO()
1234 # Don't wrap/continue long headers since we're trying to test
1235 # idempotency.
1236 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001237 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001238 self.assertEqual(text, s.getvalue())
1239
1240 def test_message_from_file(self):
1241 fp = openfile('msg_01.txt')
1242 try:
1243 text = fp.read()
1244 fp.seek(0)
1245 msg = email.message_from_file(fp)
1246 s = StringIO()
1247 # Don't wrap/continue long headers since we're trying to test
1248 # idempotency.
1249 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001250 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001251 self.assertEqual(text, s.getvalue())
1252 finally:
1253 fp.close()
1254
1255 def test_message_from_string_with_class(self):
1256 unless = self.failUnless
1257 fp = openfile('msg_01.txt')
1258 try:
1259 text = fp.read()
1260 finally:
1261 fp.close()
1262 # Create a subclass
1263 class MyMessage(Message):
1264 pass
Tim Peters527e64f2001-10-04 05:36:56 +00001265
Barry Warsaw41075852001-09-23 03:18:13 +00001266 msg = email.message_from_string(text, MyMessage)
1267 unless(isinstance(msg, MyMessage))
1268 # Try something more complicated
1269 fp = openfile('msg_02.txt')
1270 try:
1271 text = fp.read()
1272 finally:
1273 fp.close()
1274 msg = email.message_from_string(text, MyMessage)
1275 for subpart in msg.walk():
1276 unless(isinstance(subpart, MyMessage))
1277
Barry Warsaw41075852001-09-23 03:18:13 +00001278 def test_message_from_file_with_class(self):
1279 unless = self.failUnless
1280 # Create a subclass
1281 class MyMessage(Message):
1282 pass
Tim Peters527e64f2001-10-04 05:36:56 +00001283
Barry Warsaw41075852001-09-23 03:18:13 +00001284 fp = openfile('msg_01.txt')
1285 try:
1286 msg = email.message_from_file(fp, MyMessage)
1287 finally:
1288 fp.close()
1289 unless(isinstance(msg, MyMessage))
1290 # Try something more complicated
1291 fp = openfile('msg_02.txt')
1292 try:
1293 msg = email.message_from_file(fp, MyMessage)
1294 finally:
1295 fp.close()
1296 for subpart in msg.walk():
1297 unless(isinstance(subpart, MyMessage))
1298
Barry Warsawfee435a2001-10-09 19:23:57 +00001299 def test__all__(self):
1300 module = __import__('email')
1301 all = module.__all__
1302 all.sort()
Barry Warsaw16f90552002-04-16 05:06:42 +00001303 self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator',
1304 'Header', 'Iterators', 'MIMEAudio',
1305 'MIMEBase', 'MIMEImage', 'MIMEMessage',
Barry Warsaw409a4c02002-04-10 21:01:31 +00001306 'MIMEText', 'Message', 'Parser',
Barry Warsaw16f90552002-04-16 05:06:42 +00001307 'Utils', 'base64MIME',
Barry Warsaw409a4c02002-04-10 21:01:31 +00001308 'message_from_file', 'message_from_string',
1309 'quopriMIME'])
Barry Warsawfee435a2001-10-09 19:23:57 +00001310
Barry Warsaw75edc6a2001-11-09 17:46:17 +00001311 def test_formatdate(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001312 now = time.time()
1313 self.assertEqual(Utils.parsedate(Utils.formatdate(now))[:6],
1314 time.gmtime(now)[:6])
Barry Warsaw75edc6a2001-11-09 17:46:17 +00001315
Barry Warsaw4586d2c2001-11-19 18:38:42 +00001316 def test_formatdate_localtime(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001317 now = time.time()
1318 self.assertEqual(
1319 Utils.parsedate(Utils.formatdate(now, localtime=1))[:6],
1320 time.localtime(now)[:6])
Barry Warsaw75a40fc2001-11-19 16:31:06 +00001321
1322 def test_parsedate_none(self):
Barry Warsaw19c10ca2001-11-13 18:01:37 +00001323 self.assertEqual(Utils.parsedate(''), None)
1324
Barry Warsaweae36ac2001-12-20 16:37:27 +00001325 def test_parseaddr_empty(self):
1326 self.assertEqual(Utils.parseaddr('<>'), ('', ''))
Barry Warsaw409a4c02002-04-10 21:01:31 +00001327 self.assertEqual(Utils.formataddr(Utils.parseaddr('<>')), '')
1328
1329 def test_noquote_dump(self):
1330 self.assertEqual(
1331 Utils.formataddr(('A Silly Person', 'person@dom.ain')),
1332 'A Silly Person <person@dom.ain>')
1333
1334 def test_escape_dump(self):
1335 self.assertEqual(
1336 Utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')),
1337 r'"A \(Very\) Silly Person" <person@dom.ain>')
1338 a = r'A \(Special\) Person'
1339 b = 'person@dom.ain'
1340 self.assertEqual(Utils.parseaddr(Utils.formataddr((a, b))), (a, b))
1341
1342 def test_quote_dump(self):
1343 self.assertEqual(
1344 Utils.formataddr(('A Silly; Person', 'person@dom.ain')),
1345 r'"A Silly; Person" <person@dom.ain>')
1346
1347 def test_fix_eols(self):
1348 eq = self.assertEqual
1349 eq(Utils.fix_eols('hello'), 'hello')
1350 eq(Utils.fix_eols('hello\n'), 'hello\r\n')
1351 eq(Utils.fix_eols('hello\r'), 'hello\r\n')
1352 eq(Utils.fix_eols('hello\r\n'), 'hello\r\n')
1353 eq(Utils.fix_eols('hello\n\r'), 'hello\r\n\r\n')
1354
1355 def test_charset_richcomparisons(self):
1356 eq = self.assertEqual
1357 ne = self.failIfEqual
1358 cset1 = Charset()
1359 cset2 = Charset()
1360 eq(cset1, 'us-ascii')
1361 eq(cset1, 'US-ASCII')
1362 eq(cset1, 'Us-AsCiI')
1363 eq('us-ascii', cset1)
1364 eq('US-ASCII', cset1)
1365 eq('Us-AsCiI', cset1)
1366 ne(cset1, 'usascii')
1367 ne(cset1, 'USASCII')
1368 ne(cset1, 'UsAsCiI')
1369 ne('usascii', cset1)
1370 ne('USASCII', cset1)
1371 ne('UsAsCiI', cset1)
1372 eq(cset1, cset2)
1373 eq(cset2, cset1)
Barry Warsaweae36ac2001-12-20 16:37:27 +00001374
Barry Warsaw4be9ecc2002-05-22 01:52:10 +00001375 def test_getaddresses(self):
1376 eq = self.assertEqual
1377 eq(Utils.getaddresses(['aperson@dom.ain (Al Person)',
1378 'Bud Person <bperson@dom.ain>']),
1379 [('Al Person', 'aperson@dom.ain'),
1380 ('Bud Person', 'bperson@dom.ain')])
1381
Barry Warsaw41075852001-09-23 03:18:13 +00001382
Barry Warsaw08a534d2001-10-04 17:58:50 +00001383
1384# Test the iterator/generators
Barry Warsaw41075852001-09-23 03:18:13 +00001385class TestIterators(TestEmailBase):
1386 def test_body_line_iterator(self):
1387 eq = self.assertEqual
1388 # First a simple non-multipart message
1389 msg = self._msgobj('msg_01.txt')
1390 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +00001391 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +00001392 eq(len(lines), 6)
1393 eq(EMPTYSTRING.join(lines), msg.get_payload())
1394 # Now a more complicated multipart
1395 msg = self._msgobj('msg_02.txt')
1396 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +00001397 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +00001398 eq(len(lines), 43)
Barry Warsaw08a534d2001-10-04 17:58:50 +00001399 eq(EMPTYSTRING.join(lines), openfile('msg_19.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +00001400
1401 def test_typed_subpart_iterator(self):
1402 eq = self.assertEqual
1403 msg = self._msgobj('msg_04.txt')
1404 it = Iterators.typed_subpart_iterator(msg, 'text')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001405 lines = []
1406 subparts = 0
1407 for subpart in it:
1408 subparts += 1
1409 lines.append(subpart.get_payload())
1410 eq(subparts, 2)
Barry Warsaw41075852001-09-23 03:18:13 +00001411 eq(EMPTYSTRING.join(lines), """\
1412a simple kind of mirror
1413to reflect upon our own
1414a simple kind of mirror
1415to reflect upon our own
1416""")
1417
Barry Warsawcdc632c2001-10-15 04:39:02 +00001418 def test_typed_subpart_iterator_default_type(self):
1419 eq = self.assertEqual
1420 msg = self._msgobj('msg_03.txt')
1421 it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
1422 lines = []
1423 subparts = 0
1424 for subpart in it:
1425 subparts += 1
1426 lines.append(subpart.get_payload())
1427 eq(subparts, 1)
1428 eq(EMPTYSTRING.join(lines), """\
1429
1430Hi,
1431
1432Do you like this message?
1433
1434-Me
1435""")
Barry Warsaw41075852001-09-23 03:18:13 +00001436
Barry Warsaw409a4c02002-04-10 21:01:31 +00001437
Barry Warsaw08a534d2001-10-04 17:58:50 +00001438
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001439class TestParsers(unittest.TestCase):
1440 def test_header_parser(self):
1441 eq = self.assertEqual
1442 # Parse only the headers of a complex multipart MIME document
1443 p = HeaderParser()
1444 fp = openfile('msg_02.txt')
1445 msg = p.parse(fp)
1446 eq(msg['from'], 'ppp-request@zzz.org')
1447 eq(msg['to'], 'ppp@zzz.org')
1448 eq(msg.get_type(), 'multipart/mixed')
1449 eq(msg.is_multipart(), 0)
1450 self.failUnless(isinstance(msg.get_payload(), StringType))
1451
Barry Warsaw409a4c02002-04-10 21:01:31 +00001452 def test_whitespace_continuaton(self):
1453 eq = self.assertEqual
1454 # This message contains a line after the Subject: header that has only
1455 # whitespace, but it is not empty!
1456 msg = email.message_from_string("""\
1457From: aperson@dom.ain
1458To: bperson@dom.ain
1459Subject: the next line has a space on it
Barry Warsaw16f90552002-04-16 05:06:42 +00001460\x20
Barry Warsaw409a4c02002-04-10 21:01:31 +00001461Date: Mon, 8 Apr 2002 15:09:19 -0400
1462Message-ID: spam
1463
1464Here's the message body
1465""")
1466 eq(msg['subject'], 'the next line has a space on it\n ')
1467 eq(msg['message-id'], 'spam')
1468 eq(msg.get_payload(), "Here's the message body\n")
1469
Barry Warsawe0d85c82002-05-19 23:52:54 +00001470 def test_crlf_separation(self):
1471 eq = self.assertEqual
1472 fp = openfile('msg_26.txt')
1473 p = Parser()
1474 msg = p.parse(fp)
1475 eq(len(msg.get_payload()), 2)
1476 part1 = msg.get_payload(0)
1477 eq(part1.get_type(), 'text/plain')
1478 eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n')
1479 part2 = msg.get_payload(1)
1480 eq(part2.get_type(), 'application/riscos')
1481
Barry Warsawb6a92132002-06-28 23:49:33 +00001482## def test_multipart_digest_with_extra_mime_headers(self):
1483## eq = self.assertEqual
1484## fp = openfile('msg_28.txt')
1485## p = Parser()
1486## msg = p.parse(fp)
1487## self.failUnless(msg.is_multipart())
1488## eq(len(msg.get_payload()), 2)
1489## part1 = msg.get_payload(0)
1490## eq(part1.get_type(), 'text/plain')
1491## eq(part1.get_payload(), 'message 1')
1492## part2 = msg.get_payload(1)
1493## eq(part2.get_type(), 'text/plain')
1494## eq(part2.get_payload(), 'message 2')
1495
Barry Warsaw409a4c02002-04-10 21:01:31 +00001496
1497
1498class TestBase64(unittest.TestCase):
1499 def test_len(self):
1500 eq = self.assertEqual
1501 eq(base64MIME.base64_len('hello'),
1502 len(base64MIME.encode('hello', eol='')))
1503 for size in range(15):
1504 if size == 0 : bsize = 0
1505 elif size <= 3 : bsize = 4
1506 elif size <= 6 : bsize = 8
1507 elif size <= 9 : bsize = 12
1508 elif size <= 12: bsize = 16
1509 else : bsize = 20
1510 eq(base64MIME.base64_len('x'*size), bsize)
1511
1512 def test_decode(self):
1513 eq = self.assertEqual
1514 eq(base64MIME.decode(''), '')
1515 eq(base64MIME.decode('aGVsbG8='), 'hello')
1516 eq(base64MIME.decode('aGVsbG8=', 'X'), 'hello')
1517 eq(base64MIME.decode('aGVsbG8NCndvcmxk\n', 'X'), 'helloXworld')
1518
1519 def test_encode(self):
1520 eq = self.assertEqual
1521 eq(base64MIME.encode(''), '')
1522 eq(base64MIME.encode('hello'), 'aGVsbG8=\n')
1523 # Test the binary flag
1524 eq(base64MIME.encode('hello\n'), 'aGVsbG8K\n')
1525 eq(base64MIME.encode('hello\n', 0), 'aGVsbG8NCg==\n')
1526 # Test the maxlinelen arg
1527 eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40), """\
1528eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1529eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1530eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1531eHh4eCB4eHh4IA==
1532""")
1533 # Test the eol argument
1534 eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1535eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1536eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1537eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1538eHh4eCB4eHh4IA==\r
1539""")
Barry Warsaw16f90552002-04-16 05:06:42 +00001540
Barry Warsaw409a4c02002-04-10 21:01:31 +00001541 def test_header_encode(self):
1542 eq = self.assertEqual
1543 he = base64MIME.header_encode
1544 eq(he('hello'), '=?iso-8859-1?b?aGVsbG8=?=')
1545 eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8NCndvcmxk?=')
1546 # Test the charset option
1547 eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?b?aGVsbG8=?=')
1548 # Test the keep_eols flag
1549 eq(he('hello\nworld', keep_eols=1),
1550 '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=')
1551 # Test the maxlinelen argument
1552 eq(he('xxxx ' * 20, maxlinelen=40), """\
1553=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=
1554 =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=
1555 =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=
1556 =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=
1557 =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=
1558 =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
1559 # Test the eol argument
1560 eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1561=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=\r
1562 =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=\r
1563 =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=\r
1564 =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=\r
1565 =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=\r
1566 =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
1567
1568
1569
1570class TestQuopri(unittest.TestCase):
1571 def setUp(self):
1572 self.hlit = [chr(x) for x in range(ord('a'), ord('z')+1)] + \
1573 [chr(x) for x in range(ord('A'), ord('Z')+1)] + \
1574 [chr(x) for x in range(ord('0'), ord('9')+1)] + \
1575 ['!', '*', '+', '-', '/', ' ']
1576 self.hnon = [chr(x) for x in range(256) if chr(x) not in self.hlit]
1577 assert len(self.hlit) + len(self.hnon) == 256
1578 self.blit = [chr(x) for x in range(ord(' '), ord('~')+1)] + ['\t']
1579 self.blit.remove('=')
1580 self.bnon = [chr(x) for x in range(256) if chr(x) not in self.blit]
1581 assert len(self.blit) + len(self.bnon) == 256
1582
1583 def test_header_quopri_check(self):
1584 for c in self.hlit:
1585 self.failIf(quopriMIME.header_quopri_check(c))
1586 for c in self.hnon:
1587 self.failUnless(quopriMIME.header_quopri_check(c))
1588
1589 def test_body_quopri_check(self):
1590 for c in self.blit:
1591 self.failIf(quopriMIME.body_quopri_check(c))
1592 for c in self.bnon:
1593 self.failUnless(quopriMIME.body_quopri_check(c))
1594
1595 def test_header_quopri_len(self):
1596 eq = self.assertEqual
1597 hql = quopriMIME.header_quopri_len
1598 enc = quopriMIME.header_encode
1599 for s in ('hello', 'h@e@l@l@o@'):
1600 # Empty charset and no line-endings. 7 == RFC chrome
1601 eq(hql(s), len(enc(s, charset='', eol=''))-7)
1602 for c in self.hlit:
1603 eq(hql(c), 1)
1604 for c in self.hnon:
1605 eq(hql(c), 3)
1606
1607 def test_body_quopri_len(self):
1608 eq = self.assertEqual
1609 bql = quopriMIME.body_quopri_len
1610 for c in self.blit:
1611 eq(bql(c), 1)
1612 for c in self.bnon:
1613 eq(bql(c), 3)
1614
1615 def test_quote_unquote_idempotent(self):
1616 for x in range(256):
1617 c = chr(x)
1618 self.assertEqual(quopriMIME.unquote(quopriMIME.quote(c)), c)
1619
1620 def test_header_encode(self):
1621 eq = self.assertEqual
1622 he = quopriMIME.header_encode
1623 eq(he('hello'), '=?iso-8859-1?q?hello?=')
1624 eq(he('hello\nworld'), '=?iso-8859-1?q?hello=0D=0Aworld?=')
1625 # Test the charset option
1626 eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=')
1627 # Test the keep_eols flag
1628 eq(he('hello\nworld', keep_eols=1), '=?iso-8859-1?q?hello=0Aworld?=')
1629 # Test a non-ASCII character
Jack Jansen1476c272002-04-23 10:52:44 +00001630 eq(he('hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001631 # Test the maxlinelen argument
1632 eq(he('xxxx ' * 20, maxlinelen=40), """\
1633=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=
1634 =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=
1635 =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=
1636 =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=
1637 =?iso-8859-1?q?x_xxxx_xxxx_?=""")
1638 # Test the eol argument
1639 eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1640=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=\r
1641 =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=\r
1642 =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=\r
1643 =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=\r
1644 =?iso-8859-1?q?x_xxxx_xxxx_?=""")
1645
1646 def test_decode(self):
1647 eq = self.assertEqual
1648 eq(quopriMIME.decode(''), '')
1649 eq(quopriMIME.decode('hello'), 'hello')
1650 eq(quopriMIME.decode('hello', 'X'), 'hello')
1651 eq(quopriMIME.decode('hello\nworld', 'X'), 'helloXworld')
1652
1653 def test_encode(self):
1654 eq = self.assertEqual
1655 eq(quopriMIME.encode(''), '')
1656 eq(quopriMIME.encode('hello'), 'hello')
1657 # Test the binary flag
1658 eq(quopriMIME.encode('hello\r\nworld'), 'hello\nworld')
1659 eq(quopriMIME.encode('hello\r\nworld', 0), 'hello\nworld')
1660 # Test the maxlinelen arg
1661 eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40), """\
1662xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=
1663 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=
1664x xxxx xxxx xxxx xxxx=20""")
1665 # Test the eol argument
1666 eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1667xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=\r
1668 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=\r
1669x xxxx xxxx xxxx xxxx=20""")
1670 eq(quopriMIME.encode("""\
1671one line
1672
1673two line"""), """\
1674one line
1675
1676two line""")
Barry Warsaw16f90552002-04-16 05:06:42 +00001677
Barry Warsaw409a4c02002-04-10 21:01:31 +00001678
1679
1680# Test the Charset class
1681class TestCharset(unittest.TestCase):
1682 def test_idempotent(self):
1683 eq = self.assertEqual
1684 # Make sure us-ascii = no Unicode conversion
1685 c = Charset('us-ascii')
1686 s = 'Hello World!'
1687 sp = c.to_splittable(s)
1688 eq(s, c.from_splittable(sp))
1689 # test 8-bit idempotency with us-ascii
1690 s = '\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa'
1691 sp = c.to_splittable(s)
1692 eq(s, c.from_splittable(sp))
1693
1694
1695
1696# Test multilingual MIME headers.
Barry Warsawb6a92132002-06-28 23:49:33 +00001697class TestHeader(TestEmailBase):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001698 def test_simple(self):
Barry Warsawb6a92132002-06-28 23:49:33 +00001699 eq = self.ndiffAssertEqual
1700 h = Header('Hello World!')
1701 eq(h.encode(), 'Hello World!')
1702 h.append(' Goodbye World!')
1703 eq(h.encode(), 'Hello World! Goodbye World!')
1704
1705 def test_simple_surprise(self):
1706 eq = self.ndiffAssertEqual
Barry Warsaw409a4c02002-04-10 21:01:31 +00001707 h = Header('Hello World!')
1708 eq(h.encode(), 'Hello World!')
1709 h.append('Goodbye World!')
Barry Warsawb6a92132002-06-28 23:49:33 +00001710 eq(h.encode(), 'Hello World!Goodbye World!')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001711
1712 def test_header_needs_no_decoding(self):
1713 h = 'no decoding needed'
1714 self.assertEqual(decode_header(h), [(h, None)])
1715
1716 def test_long(self):
1717 h = Header("I am the very model of a modern Major-General; I've information vegetable, animal, and mineral; I know the kings of England, and I quote the fights historical from Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical; I understand equations, both the simple and quadratical; about binomial theorem I'm teeming with a lot o' news, with many cheerful facts about the square of the hypotenuse.",
1718 maxlinelen=76)
1719 for l in h.encode().split('\n '):
1720 self.failUnless(len(l) <= 76)
1721
1722 def test_multilingual(self):
1723 eq = self.assertEqual
1724 g = Charset("iso-8859-1")
1725 cz = Charset("iso-8859-2")
1726 utf8 = Charset("utf-8")
1727 g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
1728 cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
1729 utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
1730 h = Header(g_head, g)
1731 h.append(cz_head, cz)
1732 h.append(utf8_head, utf8)
1733 enc = h.encode()
1734 eq(enc, """=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
1735 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
1736 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
1737 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
1738 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
1739 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
1740 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
1741 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
1742 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
1743 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
1744 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
1745 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
1746 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
1747 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
1748 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=""")
1749 eq(decode_header(enc),
1750 [(g_head, "iso-8859-1"), (cz_head, "iso-8859-2"),
1751 (utf8_head, "utf-8")])
Barry Warsaw3fdc8892002-06-29 03:27:27 +00001752 # Test for conversion to unicode. BAW: Python 2.1 doesn't support the
1753 # __unicode__() protocol, so do things this way for compatibility.
1754 ustr = h.__unicode__()
1755 # For Python 2.2 and beyond
1756 #ustr = unicode(h)
1757 eq(ustr.encode('utf-8'),
1758 'Die Mieter treten hier ein werden mit einem Foerderband '
1759 'komfortabel den Korridor entlang, an s\xc3\xbcdl\xc3\xbcndischen '
1760 'Wandgem\xc3\xa4lden vorbei, gegen die rotierenden Klingen '
1761 'bef\xc3\xb6rdert. Finan\xc4\x8dni metropole se hroutily pod '
1762 'tlakem jejich d\xc5\xafvtipu.. \xe6\xad\xa3\xe7\xa2\xba\xe3\x81'
1763 '\xab\xe8\xa8\x80\xe3\x81\x86\xe3\x81\xa8\xe7\xbf\xbb\xe8\xa8\xb3'
1764 '\xe3\x81\xaf\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3'
1765 '\x81\xbe\xe3\x81\x9b\xe3\x82\x93\xe3\x80\x82\xe4\xb8\x80\xe9\x83'
1766 '\xa8\xe3\x81\xaf\xe3\x83\x89\xe3\x82\xa4\xe3\x83\x84\xe8\xaa\x9e'
1767 '\xe3\x81\xa7\xe3\x81\x99\xe3\x81\x8c\xe3\x80\x81\xe3\x81\x82\xe3'
1768 '\x81\xa8\xe3\x81\xaf\xe3\x81\xa7\xe3\x81\x9f\xe3\x82\x89\xe3\x82'
1769 '\x81\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b'
1770 '\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x8cWenn ist das Nunstuck git '
1771 'und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt '
1772 'gersput.\xe3\x80\x8d\xe3\x81\xa8\xe8\xa8\x80\xe3\x81\xa3\xe3\x81'
1773 '\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001774
Barry Warsawe0d85c82002-05-19 23:52:54 +00001775 def test_explicit_maxlinelen(self):
Barry Warsawb6a92132002-06-28 23:49:33 +00001776 eq = self.ndiffAssertEqual
Barry Warsawe0d85c82002-05-19 23:52:54 +00001777 hstr = 'A very long line that must get split to something other than at the 76th character boundary to test the non-default behavior'
1778 h = Header(hstr)
1779 eq(h.encode(), '''\
Barry Warsawb6a92132002-06-28 23:49:33 +00001780A very long line that must get split to something other than at the 76th
1781 character boundary to test the non-default behavior''')
Barry Warsawe0d85c82002-05-19 23:52:54 +00001782 h = Header(hstr, header_name='Subject')
1783 eq(h.encode(), '''\
1784A very long line that must get split to something other than at the
Barry Warsawb6a92132002-06-28 23:49:33 +00001785 76th character boundary to test the non-default behavior''')
Barry Warsawe0d85c82002-05-19 23:52:54 +00001786 h = Header(hstr, maxlinelen=1024, header_name='Subject')
1787 eq(h.encode(), hstr)
1788
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001789
1790
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001791def _testclasses():
1792 mod = sys.modules[__name__]
1793 return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
1794
1795
Barry Warsaw41075852001-09-23 03:18:13 +00001796def suite():
1797 suite = unittest.TestSuite()
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001798 for testclass in _testclasses():
1799 suite.addTest(unittest.makeSuite(testclass))
Barry Warsaw41075852001-09-23 03:18:13 +00001800 return suite
1801
1802
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001803def test_main():
1804 for testclass in _testclasses():
1805 test_support.run_unittest(testclass)
1806
1807
Barry Warsaw08a534d2001-10-04 17:58:50 +00001808
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001809if __name__ == '__main__':
Barry Warsaw409a4c02002-04-10 21:01:31 +00001810 unittest.main(defaultTest='suite')