/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6614.1.1 by Vincent Ladeuil
Fix assert_ being deprecated by using assertTrue.
1
# Copyright (C) 2007, 2009, 2011, 2014, 2016 Canonical Ltd
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
16
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
17
import sys
7078.11.1 by Jelmer Vernooij
Fix email tests on Python 3.
18
from email.header import decode_header
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
19
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
20
from .. import __version__ as _breezy_version
21
from ..email_message import EmailMessage
22
from ..errors import BzrBadParameterNotUnicode
7078.11.1 by Jelmer Vernooij
Fix email tests on Python 3.
23
from ..sixish import PY3, text_type
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
24
from ..smtp_connection import SMTPConnection
25
from .. import tests
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
26
27
EMPTY_MESSAGE = '''\
28
From: from@from.com
29
Subject: subject
30
To: to@to.com
31
User-Agent: Bazaar (%s)
32
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
33
''' % _breezy_version
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
34
35
_SIMPLE_MESSAGE = '''\
36
MIME-Version: 1.0
37
Content-Type: text/plain; charset="%%s"
38
Content-Transfer-Encoding: %%s
39
From: from@from.com
40
Subject: subject
41
To: to@to.com
42
User-Agent: Bazaar (%s)
43
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
44
%%s''' % _breezy_version
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
45
46
SIMPLE_MESSAGE_ASCII = _SIMPLE_MESSAGE % ('us-ascii', '7bit', 'body')
47
SIMPLE_MESSAGE_UTF8 = _SIMPLE_MESSAGE % ('utf-8', 'base64', 'YsOzZHk=\n')
2639.1.2 by John Arbash Meinel
Some cleanups for the EmailMessage class.
48
SIMPLE_MESSAGE_8BIT = _SIMPLE_MESSAGE % ('8-bit', 'base64', 'YvRkeQ==\n')
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
49
50
51
BOUNDARY = '=====123456=='
52
53
_MULTIPART_HEAD = '''\
54
Content-Type: multipart/mixed; boundary="%(boundary)s"
55
MIME-Version: 1.0
56
From: from@from.com
57
Subject: subject
58
To: to@to.com
59
User-Agent: Bazaar (%(version)s)
60
61
--%(boundary)s
62
MIME-Version: 1.0
63
Content-Type: text/plain; charset="us-ascii"
64
Content-Transfer-Encoding: 7bit
65
Content-Disposition: inline
66
67
body
7143.15.2 by Jelmer Vernooij
Run autopep8.
68
''' % {'version': _breezy_version, 'boundary': BOUNDARY}
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
69
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
70
71
def final_newline_or_not(msg):
72
    if sys.version_info >= (2, 7, 6):
73
        # Some internals of python's email module changed in an (minor)
74
        # incompatible way: a final newline is appended in 2.7.6...
7143.15.2 by Jelmer Vernooij
Run autopep8.
75
        msg += '\n'
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
76
    return msg
77
78
79
def simple_multipart_message():
80
    msg = _MULTIPART_HEAD + '--%s--' % BOUNDARY
81
    return final_newline_or_not(msg)
82
83
84
def complex_multipart_message(typ):
85
    msg = _MULTIPART_HEAD + '''\
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
86
--%(boundary)s
87
MIME-Version: 1.0
88
Content-Type: text/%%s; charset="us-ascii"; name="lines.txt"
89
Content-Transfer-Encoding: 7bit
90
Content-Disposition: inline
91
92
a
93
b
94
c
95
d
96
e
97
7143.15.2 by Jelmer Vernooij
Run autopep8.
98
--%(boundary)s--''' % {'boundary': BOUNDARY}
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
99
    msg = final_newline_or_not(msg)
100
    return msg % (typ,)
101
102
103
class TestEmailMessage(tests.TestCase):
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
104
105
    def test_empty_message(self):
106
        msg = EmailMessage('from@from.com', 'to@to.com', 'subject')
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
107
        self.assertEqualDiff(EMPTY_MESSAGE, msg.as_string())
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
108
109
    def test_simple_message(self):
110
        pairs = {
7058.4.2 by Jelmer Vernooij
Fix boundaries handling.
111
            b'body': SIMPLE_MESSAGE_ASCII,
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
112
            u'b\xf3dy': SIMPLE_MESSAGE_UTF8,
7058.4.2 by Jelmer Vernooij
Fix boundaries handling.
113
            b'b\xc3\xb3dy': SIMPLE_MESSAGE_UTF8,
114
            b'b\xf4dy': SIMPLE_MESSAGE_8BIT,
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
115
        }
116
        for body, expected in pairs.items():
117
            msg = EmailMessage('from@from.com', 'to@to.com', 'subject', body)
118
            self.assertEqualDiff(expected, msg.as_string())
119
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
120
    def test_multipart_message_simple(self):
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
121
        msg = EmailMessage('from@from.com', 'to@to.com', 'subject')
122
        msg.add_inline_attachment('body')
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
123
        self.assertEqualDiff(simple_multipart_message(),
124
                             msg.as_string(BOUNDARY))
125
126
    def test_multipart_message_complex(self):
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
127
        msg = EmailMessage('from@from.com', 'to@to.com', 'subject', 'body')
128
        msg.add_inline_attachment(u'a\nb\nc\nd\ne\n', 'lines.txt', 'x-subtype')
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
129
        self.assertEqualDiff(complex_multipart_message('x-subtype'),
130
                             msg.as_string(BOUNDARY))
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
131
132
    def test_headers_accept_unicode_and_utf8(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
133
        for user in [u'Pepe P\xe9rez <pperez@ejemplo.com>',
134
                     'Pepe P\xc3\xa9red <pperez@ejemplo.com>']:
135
            msg = EmailMessage(user, user, user)  # no exception raised
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
136
137
            for header in ['From', 'To', 'Subject']:
138
                value = msg[header]
7143.15.2 by Jelmer Vernooij
Run autopep8.
139
                value.encode('ascii')  # no UnicodeDecodeError
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
140
141
    def test_headers_reject_8bit(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
142
        for i in range(3):  # from_address, to_address, subject
143
            x = [b'"J. Random Developer" <jrandom@example.com>'] * 3
7078.11.1 by Jelmer Vernooij
Fix email tests on Python 3.
144
            x[i] = b'Pepe P\xe9rez <pperez@ejemplo.com>'
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
145
            self.assertRaises(BzrBadParameterNotUnicode, EmailMessage, *x)
146
147
    def test_multiple_destinations(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
148
        to_addresses = ['to1@to.com', 'to2@to.com', 'to3@to.com']
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
149
        msg = EmailMessage('from@from.com', to_addresses, 'subject')
7143.15.13 by Jelmer Vernooij
Fix more syntax errors.
150
        self.assertContainsRe(msg.as_string(), 'To: '
7143.15.2 by Jelmer Vernooij
Run autopep8.
151
                              + ', '.join(to_addresses))  # re.M can't be passed, so no ^$
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
152
153
    def test_retrieving_headers(self):
154
        msg = EmailMessage('from@from.com', 'to@to.com', 'subject')
155
        for header, value in [('From', 'from@from.com'), ('To', 'to@to.com'),
7143.15.2 by Jelmer Vernooij
Run autopep8.
156
                              ('Subject', 'subject')]:
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
157
            self.assertEqual(value, msg.get(header))
158
            self.assertEqual(value, msg[header])
159
        self.assertEqual(None, msg.get('Does-Not-Exist'))
160
        self.assertEqual(None, msg['Does-Not-Exist'])
161
        self.assertEqual('None', msg.get('Does-Not-Exist', 'None'))
162
163
    def test_setting_headers(self):
164
        msg = EmailMessage('from@from.com', 'to@to.com', 'subject')
165
        msg['To'] = 'to2@to.com'
166
        msg['Cc'] = 'cc@cc.com'
167
        self.assertEqual('to2@to.com', msg['To'])
168
        self.assertEqual('cc@cc.com', msg['Cc'])
169
170
    def test_address_to_encoded_header(self):
171
        def decode(s):
172
            """Convert a RFC2047-encoded string to a unicode string."""
7078.11.1 by Jelmer Vernooij
Fix email tests on Python 3.
173
            if PY3:
174
                return ''.join([chunk.decode(encoding or 'ascii')
175
                                for chunk, encoding in decode_header(s)])
176
            else:
177
                # Cope with python2 stripping whitespace.
178
                # https://bugs.python.org/issue1467619
179
                return ' '.join([chunk.decode(encoding or 'ascii')
180
                                 for chunk, encoding in decode_header(s)])
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
181
182
        address = 'jrandom@example.com'
183
        encoded = EmailMessage.address_to_encoded_header(address)
184
        self.assertEqual(address, encoded)
185
186
        address = 'J Random Developer <jrandom@example.com>'
187
        encoded = EmailMessage.address_to_encoded_header(address)
188
        self.assertEqual(address, encoded)
189
190
        address = '"J. Random Developer" <jrandom@example.com>'
191
        encoded = EmailMessage.address_to_encoded_header(address)
192
        self.assertEqual(address, encoded)
193
7143.15.2 by Jelmer Vernooij
Run autopep8.
194
        address = u'Pepe P\xe9rez <pperez@ejemplo.com>'  # unicode ok
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
195
        encoded = EmailMessage.address_to_encoded_header(address)
7143.15.2 by Jelmer Vernooij
Run autopep8.
196
        # addr must be unencoded
197
        self.assertTrue('pperez@ejemplo.com' in encoded)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
198
        self.assertEqual(address, decode(encoded))
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
199
7143.15.2 by Jelmer Vernooij
Run autopep8.
200
        address = b'Pepe P\xe9rez <pperez@ejemplo.com>'  # ISO-8859-1 not ok
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
201
        self.assertRaises(BzrBadParameterNotUnicode,
7143.15.2 by Jelmer Vernooij
Run autopep8.
202
                          EmailMessage.address_to_encoded_header, address)
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
203
204
    def test_string_with_encoding(self):
205
        pairs = {
7143.15.2 by Jelmer Vernooij
Run autopep8.
206
            u'Pepe': (b'Pepe', 'ascii'),
207
            u'P\xe9rez': (b'P\xc3\xa9rez', 'utf-8'),
208
            b'P\xc3\xa9rez': (b'P\xc3\xa9rez', 'utf-8'),
209
            b'P\xe8rez': (b'P\xe8rez', '8-bit'),
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
210
        }
2625.6.3 by Adeodato Simó
Changes after review by John.
211
        for string_, pair in pairs.items():
212
            self.assertEqual(pair, EmailMessage.string_with_encoding(string_))
6592.1.1 by Vincent Ladeuil
Fix minor incompatible change in email python 2.7.6 module.
213
214
215
class TestSend(tests.TestCase):
216
217
    def setUp(self):
218
        super(TestSend, self).setUp()
219
        self.messages = []
220
221
        def send_as_append(_self, msg):
222
            self.messages.append(msg.as_string(BOUNDARY))
223
224
        self.overrideAttr(SMTPConnection, 'send_email', send_as_append)
225
226
    def send_email(self, attachment=None, attachment_filename=None,
227
                   attachment_mime_subtype='plain'):
228
        class FakeConfig:
229
            def get(self, option):
230
                return None
231
232
        EmailMessage.send(FakeConfig(), 'from@from.com', 'to@to.com',
233
                          'subject', 'body',
234
                          attachment=attachment,
235
                          attachment_filename=attachment_filename,
236
                          attachment_mime_subtype=attachment_mime_subtype)
237
238
    def assertMessage(self, expected):
239
        self.assertLength(1, self.messages)
240
        self.assertEqualDiff(expected, self.messages[0])
241
242
    def test_send_plain(self):
243
        self.send_email(u'a\nb\nc\nd\ne\n', 'lines.txt')
244
        self.assertMessage(complex_multipart_message('plain'))
245
246
    def test_send_patch(self):
247
        self.send_email(u'a\nb\nc\nd\ne\n', 'lines.txt', 'x-patch')
248
        self.assertMessage(complex_multipart_message('x-patch'))
249
250
    def test_send_simple(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
251
        self.send_email()
252
        self.assertMessage(SIMPLE_MESSAGE_ASCII)