Mercurial > p > roundup > code
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 ></body></html>\n', | 58 'text/html': ' name="bar.html"\n<html><body>bar ></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 |
