/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_rio.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-19 13:16:11 UTC
  • mto: (6968.4.3 git-archive)
  • mto: This revision was merged to the branch mainline in revision 6972.
  • Revision ID: jelmer@jelmer.uk-20180519131611-l9h9ud41j7qg1m03
Move tar/zip to breezy.archive.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 2016 Canonical Ltd
 
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for rio serialization
 
18
 
 
19
A simple, reproducible structured IO format.
 
20
 
 
21
rio itself works in Unicode strings.  It is typically encoded to UTF-8,
 
22
but this depends on the transport.
 
23
"""
 
24
 
 
25
import re
 
26
from tempfile import TemporaryFile
 
27
 
 
28
from breezy import (
 
29
    rio,
 
30
    )
 
31
from breezy.tests import TestCase
 
32
from breezy.rio import (
 
33
    RioReader,
 
34
    Stanza,
 
35
    read_stanza,
 
36
    read_stanzas,
 
37
    rio_file,
 
38
    )
 
39
 
 
40
 
 
41
class TestRio(TestCase):
 
42
 
 
43
    def test_stanza(self):
 
44
        """Construct rio stanza in memory"""
 
45
        s = Stanza(number='42', name="fred")
 
46
        self.assertTrue('number' in s)
 
47
        self.assertFalse('color' in s)
 
48
        self.assertFalse('42' in s)
 
49
        self.assertEqual(list(s.iter_pairs()),
 
50
                [('name', 'fred'), ('number', '42')])
 
51
        self.assertEqual(s.get('number'), '42')
 
52
        self.assertEqual(s.get('name'), 'fred')
 
53
 
 
54
    def test_empty_value(self):
 
55
        """Serialize stanza with empty field"""
 
56
        s = Stanza(empty='')
 
57
        self.assertEquals(s.to_string(),
 
58
                b"empty: \n")
 
59
 
 
60
    def test_to_lines(self):
 
61
        """Write simple rio stanza to string"""
 
62
        s = Stanza(number='42', name='fred')
 
63
        self.assertEqual(list(s.to_lines()),
 
64
                [b'name: fred\n',
 
65
                 b'number: 42\n'])
 
66
 
 
67
    def test_as_dict(self):
 
68
        """Convert rio Stanza to dictionary"""
 
69
        s = Stanza(number='42', name='fred')
 
70
        sd = s.as_dict()
 
71
        self.assertEqual(sd, dict(number='42', name='fred'))
 
72
 
 
73
    def test_to_file(self):
 
74
        """Write rio to file"""
 
75
        tmpf = TemporaryFile()
 
76
        s = Stanza(a_thing='something with "quotes like \\"this\\""', number='42', name='fred')
 
77
        s.write(tmpf)
 
78
        tmpf.seek(0)
 
79
        self.assertEqual(tmpf.read(), b'''\
 
80
a_thing: something with "quotes like \\"this\\""
 
81
name: fred
 
82
number: 42
 
83
''')
 
84
 
 
85
    def test_multiline_string(self):
 
86
        tmpf = TemporaryFile()
 
87
        s = Stanza(motto="war is peace\nfreedom is slavery\nignorance is strength")
 
88
        s.write(tmpf)
 
89
        tmpf.seek(0)
 
90
        self.assertEqual(tmpf.read(), b'''\
 
91
motto: war is peace
 
92
\tfreedom is slavery
 
93
\tignorance is strength
 
94
''')
 
95
        tmpf.seek(0)
 
96
        s2 = read_stanza(tmpf)
 
97
        self.assertEqual(s, s2)
 
98
 
 
99
    def test_read_stanza(self):
 
100
        """Load stanza from string"""
 
101
        lines = b"""\
 
102
revision: mbp@sourcefrog.net-123-abc
 
103
timestamp: 1130653962
 
104
timezone: 36000
 
105
committer: Martin Pool <mbp@test.sourcefrog.net>
 
106
""".splitlines(True)
 
107
        s = read_stanza(lines)
 
108
        self.assertTrue('revision' in s)
 
109
        self.assertEqual(s.get('revision'), 'mbp@sourcefrog.net-123-abc')
 
110
        self.assertEqual(list(s.iter_pairs()),
 
111
                [('revision', 'mbp@sourcefrog.net-123-abc'),
 
112
                 ('timestamp', '1130653962'),
 
113
                 ('timezone', '36000'),
 
114
                 ('committer', "Martin Pool <mbp@test.sourcefrog.net>")])
 
115
        self.assertEqual(len(s), 4)
 
116
 
 
117
    def test_repeated_field(self):
 
118
        """Repeated field in rio"""
 
119
        s = Stanza()
 
120
        for k, v in [('a', '10'), ('b', '20'), ('a', '100'), ('b', '200'),
 
121
                     ('a', '1000'), ('b', '2000')]:
 
122
            s.add(k, v)
 
123
        s2 = read_stanza(s.to_lines())
 
124
        self.assertEqual(s, s2)
 
125
        self.assertEqual(s.get_all('a'), ['10', '100', '1000'])
 
126
        self.assertEqual(s.get_all('b'), ['20', '200', '2000'])
 
127
 
 
128
    def test_backslash(self):
 
129
        s = Stanza(q='\\')
 
130
        t = s.to_string()
 
131
        self.assertEqual(t, b'q: \\\n')
 
132
        s2 = read_stanza(s.to_lines())
 
133
        self.assertEqual(s, s2)
 
134
 
 
135
    def test_blank_line(self):
 
136
        s = Stanza(none='', one='\n', two='\n\n')
 
137
        self.assertEqual(s.to_string(), b"""\
 
138
none:\x20
 
139
one:\x20
 
140
\t
 
141
two:\x20
 
142
\t
 
143
\t
 
144
""")
 
145
        s2 = read_stanza(s.to_lines())
 
146
        self.assertEqual(s, s2)
 
147
 
 
148
    def test_whitespace_value(self):
 
149
        s = Stanza(space=' ', tabs='\t\t\t', combo='\n\t\t\n')
 
150
        self.assertEqual(s.to_string(), b"""\
 
151
combo:\x20
 
152
\t\t\t
 
153
\t
 
154
space:\x20\x20
 
155
tabs: \t\t\t
 
156
""")
 
157
        s2 = read_stanza(s.to_lines())
 
158
        self.assertEqual(s, s2)
 
159
        self.rio_file_stanzas([s])
 
160
 
 
161
    def test_quoted(self):
 
162
        """rio quoted string cases"""
 
163
        s = Stanza(q1='"hello"',
 
164
                   q2=' "for',
 
165
                   q3='\n\n"for"\n',
 
166
                   q4='for\n"\nfor',
 
167
                   q5='\n',
 
168
                   q6='"',
 
169
                   q7='""',
 
170
                   q8='\\',
 
171
                   q9='\\"\\"',
 
172
                   )
 
173
        s2 = read_stanza(s.to_lines())
 
174
        self.assertEqual(s, s2)
 
175
        # apparent bug in read_stanza
 
176
        # s3 = read_stanza(self.stanzas_to_str([s]))
 
177
        # self.assertEqual(s, s3)
 
178
 
 
179
    def test_read_empty(self):
 
180
        """Detect end of rio file"""
 
181
        s = read_stanza([])
 
182
        self.assertEqual(s, None)
 
183
        self.assertTrue(s is None)
 
184
 
 
185
    def test_read_nul_byte(self):
 
186
        """File consisting of a nul byte causes an error."""
 
187
        self.assertRaises(ValueError, read_stanza, [b'\0'])
 
188
 
 
189
    def test_read_nul_bytes(self):
 
190
        """File consisting of many nul bytes causes an error."""
 
191
        self.assertRaises(ValueError, read_stanza, [b'\0' * 100])
 
192
 
 
193
    def test_read_iter(self):
 
194
        """Read several stanzas from file"""
 
195
        tmpf = TemporaryFile()
 
196
        tmpf.write(b"""\
 
197
version_header: 1
 
198
 
 
199
name: foo
 
200
val: 123
 
201
 
 
202
name: bar
 
203
val: 129319
 
204
""")
 
205
        tmpf.seek(0)
 
206
        reader = read_stanzas(tmpf)
 
207
        read_iter = iter(reader)
 
208
        stuff = list(reader)
 
209
        self.assertEqual(stuff,
 
210
                [ Stanza(version_header='1'),
 
211
                  Stanza(name="foo", val='123'),
 
212
                  Stanza(name="bar", val='129319'), ])
 
213
 
 
214
    def test_read_several(self):
 
215
        """Read several stanzas from file"""
 
216
        tmpf = TemporaryFile()
 
217
        tmpf.write(b"""\
 
218
version_header: 1
 
219
 
 
220
name: foo
 
221
val: 123
 
222
 
 
223
name: quoted
 
224
address:   "Willowglen"
 
225
\t  42 Wallaby Way
 
226
\t  Sydney
 
227
 
 
228
name: bar
 
229
val: 129319
 
230
""")
 
231
        tmpf.seek(0)
 
232
        s = read_stanza(tmpf)
 
233
        self.assertEqual(s, Stanza(version_header='1'))
 
234
        s = read_stanza(tmpf)
 
235
        self.assertEqual(s, Stanza(name="foo", val='123'))
 
236
        s = read_stanza(tmpf)
 
237
        self.assertEqual(s.get('name'), 'quoted')
 
238
        self.assertEqual(
 
239
            s.get('address'), '  "Willowglen"\n  42 Wallaby Way\n  Sydney')
 
240
        s = read_stanza(tmpf)
 
241
        self.assertEqual(s, Stanza(name="bar", val='129319'))
 
242
        s = read_stanza(tmpf)
 
243
        self.assertEqual(s, None)
 
244
        self.check_rio_file(tmpf)
 
245
 
 
246
    def check_rio_file(self, real_file):
 
247
        real_file.seek(0)
 
248
        read_write = rio_file(RioReader(real_file)).read()
 
249
        real_file.seek(0)
 
250
        self.assertEqual(read_write, real_file.read())
 
251
 
 
252
    @staticmethod
 
253
    def stanzas_to_str(stanzas):
 
254
        return rio_file(stanzas).read()
 
255
 
 
256
    def rio_file_stanzas(self, stanzas):
 
257
        new_stanzas = list(RioReader(rio_file(stanzas)))
 
258
        self.assertEqual(new_stanzas, stanzas)
 
259
 
 
260
    def test_tricky_quoted(self):
 
261
        tmpf = TemporaryFile()
 
262
        tmpf.write(b'''\
 
263
s: "one"
 
264
 
 
265
s:\x20
 
266
\t"one"
 
267
\t
 
268
 
 
269
s: "
 
270
 
 
271
s: ""
 
272
 
 
273
s: """
 
274
 
 
275
s:\x20
 
276
\t
 
277
 
 
278
s: \\
 
279
 
 
280
s:\x20
 
281
\t\\
 
282
\t\\\\
 
283
\t
 
284
 
 
285
s: word\\
 
286
 
 
287
s: quote"
 
288
 
 
289
s: backslashes\\\\\\
 
290
 
 
291
s: both\\\"
 
292
 
 
293
''')
 
294
        tmpf.seek(0)
 
295
        expected_vals = ['"one"',
 
296
            '\n"one"\n',
 
297
            '"',
 
298
            '""',
 
299
            '"""',
 
300
            '\n',
 
301
            '\\',
 
302
            '\n\\\n\\\\\n',
 
303
            'word\\',
 
304
            'quote\"',
 
305
            'backslashes\\\\\\',
 
306
            'both\\\"',
 
307
            ]
 
308
        for expected in expected_vals:
 
309
            stanza = read_stanza(tmpf)
 
310
            self.rio_file_stanzas([stanza])
 
311
            self.assertEqual(len(stanza), 1)
 
312
            self.assertEqual(stanza.get('s'), expected)
 
313
 
 
314
    def test_write_empty_stanza(self):
 
315
        """Write empty stanza"""
 
316
        l = list(Stanza().to_lines())
 
317
        self.assertEqual(l, [])
 
318
 
 
319
    def test_rio_raises_type_error(self):
 
320
        """TypeError on adding invalid type to Stanza"""
 
321
        s = Stanza()
 
322
        self.assertRaises(TypeError, s.add, 'foo', {})
 
323
 
 
324
    def test_rio_raises_type_error_key(self):
 
325
        """TypeError on adding invalid type to Stanza"""
 
326
        s = Stanza()
 
327
        self.assertRaises(TypeError, s.add, 10, {})
 
328
 
 
329
    def test_rio_unicode(self):
 
330
        uni_data = u'\N{KATAKANA LETTER O}'
 
331
        s = Stanza(foo=uni_data)
 
332
        self.assertEqual(s.get('foo'), uni_data)
 
333
        raw_lines = s.to_lines()
 
334
        self.assertEqual(raw_lines,
 
335
                [b'foo: ' + uni_data.encode('utf-8') + b'\n'])
 
336
        new_s = read_stanza(raw_lines)
 
337
        self.assertEqual(new_s.get('foo'), uni_data)
 
338
 
 
339
    def test_rio_to_unicode(self):
 
340
        uni_data = u'\N{KATAKANA LETTER O}'
 
341
        s = Stanza(foo=uni_data)
 
342
        unicode_str = s.to_unicode()
 
343
        self.assertEqual(u'foo: %s\n' % (uni_data,), unicode_str)
 
344
        new_s = rio.read_stanza_unicode(unicode_str.splitlines(True))
 
345
        self.assertEqual(uni_data, new_s.get('foo'))
 
346
 
 
347
    def test_nested_rio_unicode(self):
 
348
        uni_data = u'\N{KATAKANA LETTER O}'
 
349
        s = Stanza(foo=uni_data)
 
350
        parent_stanza = Stanza(child=s.to_unicode())
 
351
        raw_lines = parent_stanza.to_lines()
 
352
        self.assertEqual([b'child: foo: ' + uni_data.encode('utf-8') + b'\n',
 
353
                          b'\t\n',
 
354
                         ], raw_lines)
 
355
        new_parent = read_stanza(raw_lines)
 
356
        child_text = new_parent.get('child')
 
357
        self.assertEqual(u'foo: %s\n' % uni_data, child_text)
 
358
        new_child = rio.read_stanza_unicode(child_text.splitlines(True))
 
359
        self.assertEqual(uni_data, new_child.get('foo'))
 
360
 
 
361
    def mail_munge(self, lines, dos_nl=True):
 
362
        new_lines = []
 
363
        for line in lines:
 
364
            line = re.sub(b' *\n', b'\n', line)
 
365
            if dos_nl:
 
366
                line = re.sub(b'([^\r])\n', b'\\1\r\n', line)
 
367
            new_lines.append(line)
 
368
        return new_lines
 
369
 
 
370
    def test_patch_rio(self):
 
371
        stanza = Stanza(data='#\n\r\\r ', space=' ' * 255, hash='#' * 255)
 
372
        lines = rio.to_patch_lines(stanza)
 
373
        for line in lines:
 
374
            self.assertContainsRe(line, b'^# ')
 
375
            self.assertTrue(72 >= len(line))
 
376
        for line in rio.to_patch_lines(stanza, max_width=12):
 
377
            self.assertTrue(12 >= len(line))
 
378
        new_stanza = rio.read_patch_stanza(self.mail_munge(lines,
 
379
                                                           dos_nl=False))
 
380
        lines = self.mail_munge(lines)
 
381
        new_stanza = rio.read_patch_stanza(lines)
 
382
        self.assertEqual('#\n\r\\r ', new_stanza.get('data'))
 
383
        self.assertEqual(' '* 255, new_stanza.get('space'))
 
384
        self.assertEqual('#'* 255, new_stanza.get('hash'))
 
385
 
 
386
    def test_patch_rio_linebreaks(self):
 
387
        stanza = Stanza(breaktest='linebreak -/'*30)
 
388
        self.assertContainsRe(rio.to_patch_lines(stanza, 71)[0],
 
389
                              b'linebreak\\\\\n')
 
390
        stanza = Stanza(breaktest='linebreak-/'*30)
 
391
        self.assertContainsRe(rio.to_patch_lines(stanza, 70)[0],
 
392
                              b'linebreak-\\\\\n')
 
393
        stanza = Stanza(breaktest='linebreak/'*30)
 
394
        self.assertContainsRe(rio.to_patch_lines(stanza, 70)[0],
 
395
                              b'linebreak\\\\\n')