Code

5797dd21f35f53e1c2b3c52ec769a63c8806c61c
[roundup.git] / test / test_multipart.py
1 #
2 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
3 # This module is free software, and you may redistribute it and/or modify
4 # under the same terms as Python, so long as this copyright message and
5 # disclaimer are retained in their original form.
6 #
7 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
8 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
9 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
10 # POSSIBILITY OF SUCH DAMAGE.
11 #
12 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
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"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17
18 # $Id: test_multipart.py,v 1.8 2007-09-22 07:25:35 jpend Exp $ 
20 import unittest
21 from cStringIO import StringIO
23 from roundup.mailgw import Message
25 class TestMessage(Message):
26     table = {'multipart/signed': '    boundary="boundary-%(indent)s";\n',
27              'multipart/mixed': '    boundary="boundary-%(indent)s";\n',
28              'multipart/alternative': '    boundary="boundary-%(indent)s";\n',
29              'text/plain': '    name="foo.txt"\nfoo\n',
30              'application/pgp-signature': '    name="foo.gpg"\nfoo\n',
31              'application/pdf': '    name="foo.pdf"\nfoo\n',
32              'message/rfc822': 'Subject: foo\n\nfoo\n'}
34     def __init__(self, spec):
35         """Create a basic MIME message according to 'spec'.
37         Each line of a spec has one content-type, which is optionally indented.
38         The indentation signifies how deep in the MIME hierarchy the
39         content-type is.
41         """
42         parts = []
43         for line in spec.splitlines():
44             content_type = line.strip()
45             if not content_type:
46                 continue
48             indent = self.getIndent(line)
49             if indent:
50                 parts.append('\n--boundary-%s\n' % indent)
51             parts.append('Content-type: %s;\n' % content_type)
52             parts.append(self.table[content_type] % {'indent': indent + 1})
54         Message.__init__(self, StringIO(''.join(parts)))
56     def getIndent(self, line):
57         """Get the current line's indentation, using four-space indents."""
58         count = 0
59         for char in line:
60             if char != ' ':
61                 break
62             count += 1
63         return count / 4
65 class MultipartTestCase(unittest.TestCase):
66     def setUp(self):
67         self.fp = StringIO()
68         w = self.fp.write
69         w('Content-Type: multipart/mixed; boundary="foo"\r\n\r\n')
70         w('This is a multipart message. Ignore this bit.\r\n')
71         w('\r\n--foo\r\n')
73         w('Content-Type: text/plain\r\n\r\n')
74         w('Hello, world!\r\n')
75         w('\r\n')
76         w('Blah blah\r\n')
77         w('foo\r\n')
78         w('-foo\r\n')
79         w('\r\n--foo\r\n')
81         w('Content-Type: multipart/alternative; boundary="bar"\r\n\r\n')
82         w('This is a multipart message. Ignore this bit.\r\n')
83         w('\r\n--bar\r\n')
85         w('Content-Type: text/plain\r\n\r\n')
86         w('Hello, world!\r\n')
87         w('\r\n')
88         w('Blah blah\r\n')
89         w('\r\n--bar\r\n')
91         w('Content-Type: text/html\r\n\r\n')
92         w('<b>Hello, world!</b>\r\n')
93         w('\r\n--bar--\r\n')
94         w('\r\n--foo\r\n')
96         w('Content-Type: text/plain\r\n\r\n')
97         w('Last bit\n')
98         w('\r\n--foo--\r\n')
99         self.fp.seek(0)
101     def testMultipart(self):
102         m = Message(self.fp)
103         self.assert_(m is not None)
105         # skip the first bit
106         p = m.getpart()
107         self.assert_(p is not None)
108         self.assertEqual(p.fp.read(),
109             'This is a multipart message. Ignore this bit.\r\n')
111         # first text/plain
112         p = m.getpart()
113         self.assert_(p is not None)
114         self.assertEqual(p.gettype(), 'text/plain')
115         self.assertEqual(p.fp.read(),
116             'Hello, world!\r\n\r\nBlah blah\r\nfoo\r\n-foo\r\n')
118         # sub-multipart
119         p = m.getpart()
120         self.assert_(p is not None)
121         self.assertEqual(p.gettype(), 'multipart/alternative')
123         # sub-multipart text/plain
124         q = p.getpart()
125         self.assert_(q is not None)
126         q = p.getpart()
127         self.assert_(q is not None)
128         self.assertEqual(q.gettype(), 'text/plain')
129         self.assertEqual(q.fp.read(), 'Hello, world!\r\n\r\nBlah blah\r\n')
131         # sub-multipart text/html
132         q = p.getpart()
133         self.assert_(q is not None)
134         self.assertEqual(q.gettype(), 'text/html')
135         self.assertEqual(q.fp.read(), '<b>Hello, world!</b>\r\n')
137         # sub-multipart end
138         q = p.getpart()
139         self.assert_(q is None)
141         # final text/plain
142         p = m.getpart()
143         self.assert_(p is not None)
144         self.assertEqual(p.gettype(), 'text/plain')
145         self.assertEqual(p.fp.read(),
146             'Last bit\n')
148         # end
149         p = m.getpart()
150         self.assert_(p is None)
152     def TestExtraction(self, spec, expected):
153         self.assertEqual(TestMessage(spec).extract_content(), expected)
155     def testTextPlain(self):
156         self.TestExtraction('text/plain', ('foo\n', []))
158     def testAttachedTextPlain(self):
159         self.TestExtraction("""
160 multipart/mixed
161     text/plain
162     text/plain""",
163                   ('foo\n',
164                    [('foo.txt', 'text/plain', 'foo\n')]))
166     def testMultipartMixed(self):
167         self.TestExtraction("""
168 multipart/mixed
169     text/plain
170     application/pdf""",
171                   ('foo\n',
172                    [('foo.pdf', 'application/pdf', 'foo\n')]))
174     def testMultipartAlternative(self):
175         self.TestExtraction("""
176 multipart/alternative
177     text/plain
178     application/pdf
179 """, ('foo\n', [('foo.pdf', 'application/pdf', 'foo\n')]))
181     def testDeepMultipartAlternative(self):
182         self.TestExtraction("""
183 multipart/mixed
184     multipart/alternative
185         text/plain
186         application/pdf
187 """, ('foo\n', [('foo.pdf', 'application/pdf', 'foo\n')]))
189     def testSignedText(self):
190         self.TestExtraction("""
191 multipart/signed
192     text/plain
193     application/pgp-signature""", ('foo\n', []))
195     def testSignedAttachments(self):
196         self.TestExtraction("""
197 multipart/signed
198     multipart/mixed
199         text/plain
200         application/pdf
201     application/pgp-signature""",
202                   ('foo\n',
203                    [('foo.pdf', 'application/pdf', 'foo\n')]))
205     def testAttachedSignature(self):
206         self.TestExtraction("""
207 multipart/mixed
208     text/plain
209     application/pgp-signature""",
210                   ('foo\n',
211                    [('foo.gpg', 'application/pgp-signature', 'foo\n')]))
213     def testMessageRfc822(self):
214         self.TestExtraction("""
215 multipart/mixed
216     message/rfc822""",
217                   (None,
218                    [('foo', 'message/rfc822', 'foo\n')]))
220 def test_suite():
221     suite = unittest.TestSuite()
222     suite.addTest(unittest.makeSuite(MultipartTestCase))
223     return suite
225 if __name__ == '__main__':
226     runner = unittest.TextTestRunner()
227     unittest.main(testRunner=runner)
230 # vim: set filetype=python ts=4 sw=4 et si