comparison test/test_multipart.py @ 5493:725266c03eab

updated mailgw to no longer use mimetools based on jerrykan's patch
author Christof Meerwald <cmeerw@cmeerw.org>
date Sun, 12 Aug 2018 16:15:10 +0100
parents 55f09ca366c4
children 081be318661b
comparison
equal deleted inserted replaced
5492:6b0c542642be 5493:725266c03eab
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 17
18 import email
18 import unittest 19 import unittest
19 from roundup.anypy.strings import StringIO 20 from roundup.anypy.strings import StringIO
20 21
21 from roundup.mailgw import Message 22 from roundup.mailgw import RoundupMessage
22 23
23 class ExampleMessage(Message): 24 def gen_message(spec):
25 """Create a basic MIME message according to 'spec'.
26
27 Each line of a spec has one content-type, which is optionally indented.
28 The indentation signifies how deep in the MIME hierarchy the
29 content-type is.
30
31 """
32
33 def getIndent(line):
34 """Get the current line's indentation, using four-space indents."""
35 count = 0
36 for char in line:
37 if char != ' ':
38 break
39 count += 1
40 return count // 4
41
24 # A note on message/rfc822: The content of such an attachment is an 42 # A note on message/rfc822: The content of such an attachment is an
25 # email with at least one header line. RFC2046 tells us: """ A 43 # email with at least one header line. RFC2046 tells us: """ A
26 # media type of "message/rfc822" indicates that the body contains an 44 # media type of "message/rfc822" indicates that the body contains an
27 # encapsulated message, with the syntax of an RFC 822 message. 45 # encapsulated message, with the syntax of an RFC 822 message.
28 # However, unlike top-level RFC 822 messages, the restriction that 46 # However, unlike top-level RFC 822 messages, the restriction that
40 'text/html': ' name="bar.html"\n<html><body>bar &gt;</body></html>\n', 58 'text/html': ' name="bar.html"\n<html><body>bar &gt;</body></html>\n',
41 'application/pgp-signature': ' name="foo.gpg"\nfoo\n', 59 'application/pgp-signature': ' name="foo.gpg"\nfoo\n',
42 'application/pdf': ' name="foo.pdf"\nfoo\n', 60 'application/pdf': ' name="foo.pdf"\nfoo\n',
43 'message/rfc822': '\nSubject: foo\n\nfoo\n'} 61 'message/rfc822': '\nSubject: foo\n\nfoo\n'}
44 62
45 def __init__(self, spec): 63 parts = []
46 """Create a basic MIME message according to 'spec'. 64 for line in spec.splitlines():
47 65 content_type = line.strip()
48 Each line of a spec has one content-type, which is optionally indented. 66 if not content_type:
49 The indentation signifies how deep in the MIME hierarchy the 67 continue
50 content-type is. 68
51 69 indent = getIndent(line)
52 """ 70 if indent:
53 parts = [] 71 parts.append('\n--boundary-%s\n' % indent)
54 for line in spec.splitlines(): 72 parts.append('Content-type: %s;\n' % content_type)
55 content_type = line.strip() 73 parts.append(table[content_type] % {'indent': indent + 1})
56 if not content_type: 74
57 continue 75 for i in range(indent, 0, -1):
58 76 parts.append('\n--boundary-%s--\n' % i)
59 indent = self.getIndent(line) 77
60 if indent: 78 return email.message_from_file(StringIO(''.join(parts)), RoundupMessage)
61 parts.append('\n--boundary-%s\n' % indent)
62 parts.append('Content-type: %s;\n' % content_type)
63 parts.append(self.table[content_type] % {'indent': indent + 1})
64
65 Message.__init__(self, StringIO(''.join(parts)))
66
67 def getIndent(self, line):
68 """Get the current line's indentation, using four-space indents."""
69 count = 0
70 for char in line:
71 if char != ' ':
72 break
73 count += 1
74 return count // 4
75 79
76 class MultipartTestCase(unittest.TestCase): 80 class MultipartTestCase(unittest.TestCase):
77 def setUp(self): 81 def setUp(self):
78 self.fp = StringIO() 82 self.fp = StringIO()
79 w = self.fp.write 83 w = self.fp.write
108 w('Last bit\n') 112 w('Last bit\n')
109 w('\r\n--foo--\r\n') 113 w('\r\n--foo--\r\n')
110 self.fp.seek(0) 114 self.fp.seek(0)
111 115
112 def testMultipart(self): 116 def testMultipart(self):
113 m = Message(self.fp) 117 m = email.message_from_file(self.fp, RoundupMessage)
114 self.assert_(m is not None) 118 self.assert_(m is not None)
115 119
116 # skip the first bit 120 it = iter(m.get_payload())
117 p = m.getpart() 121
122 # first text/plain
123 p = next(it, None)
118 self.assert_(p is not None) 124 self.assert_(p is not None)
119 self.assertEqual(p.fp.read(), 125 self.assertEqual(p.get_content_type(), 'text/plain')
120 'This is a multipart message. Ignore this bit.\r\n') 126 self.assertEqual(p.get_payload(),
121 127 'Hello, world!\r\n\r\nBlah blah\r\nfoo\r\n-foo\r\n')
122 # first text/plain 128
123 p = m.getpart() 129 # sub-multipart
130 p = next(it, None)
124 self.assert_(p is not None) 131 self.assert_(p is not None)
125 self.assertEqual(p.gettype(), 'text/plain') 132 self.assertEqual(p.get_content_type(), 'multipart/alternative')
126 self.assertEqual(p.fp.read(), 133
127 'Hello, world!\r\n\r\nBlah blah\r\nfoo\r\n-foo\r\n') 134 # sub-multipart text/plain
128 135 qit = iter(p.get_payload())
129 # sub-multipart 136 q = next(qit, None)
130 p = m.getpart() 137 self.assert_(q is not None)
138 self.assertEqual(q.get_content_type(), 'text/plain')
139 self.assertEqual(q.get_payload(), 'Hello, world!\r\n\r\nBlah blah\r\n')
140
141 # sub-multipart text/html
142 q = next(qit, None)
143 self.assert_(q is not None)
144 self.assertEqual(q.get_content_type(), 'text/html')
145 self.assertEqual(q.get_payload(), '<b>Hello, world!</b>\r\n')
146
147 # sub-multipart end
148 q = next(qit, None)
149 self.assert_(q is None)
150
151 # final text/plain
152 p = next(it, None)
131 self.assert_(p is not None) 153 self.assert_(p is not None)
132 self.assertEqual(p.gettype(), 'multipart/alternative') 154 self.assertEqual(p.get_content_type(), 'text/plain')
133 155 self.assertEqual(p.get_payload(),
134 # sub-multipart text/plain
135 q = p.getpart()
136 self.assert_(q is not None)
137 q = p.getpart()
138 self.assert_(q is not None)
139 self.assertEqual(q.gettype(), 'text/plain')
140 self.assertEqual(q.fp.read(), 'Hello, world!\r\n\r\nBlah blah\r\n')
141
142 # sub-multipart text/html
143 q = p.getpart()
144 self.assert_(q is not None)
145 self.assertEqual(q.gettype(), 'text/html')
146 self.assertEqual(q.fp.read(), '<b>Hello, world!</b>\r\n')
147
148 # sub-multipart end
149 q = p.getpart()
150 self.assert_(q is None)
151
152 # final text/plain
153 p = m.getpart()
154 self.assert_(p is not None)
155 self.assertEqual(p.gettype(), 'text/plain')
156 self.assertEqual(p.fp.read(),
157 'Last bit\n') 156 'Last bit\n')
158 157
159 # end 158 # end
160 p = m.getpart() 159 p = next(it, None)
161 self.assert_(p is None) 160 self.assert_(p is None)
162 161
163 def TestExtraction(self, spec, expected, convert_html_with=False): 162 def TestExtraction(self, spec, expected, convert_html_with=False):
164 if convert_html_with: 163 if convert_html_with:
165 from roundup.dehtml import dehtml 164 from roundup.dehtml import dehtml
166 html2text=dehtml(convert_html_with).html2text 165 html2text=dehtml(convert_html_with).html2text
167 else: 166 else:
168 html2text=None 167 html2text=None
169 168
170 self.assertEqual(ExampleMessage(spec).extract_content( 169 self.assertEqual(gen_message(spec).extract_content(
171 html2text=html2text), expected) 170 html2text=html2text), expected)
172 171
173 def testTextPlain(self): 172 def testTextPlain(self):
174 self.TestExtraction('text/plain', ('foo\n', [], False)) 173 self.TestExtraction('text/plain', ('foo\n', [], False))
175 174

Roundup Issue Tracker: http://roundup-tracker.org/