/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: 2020-05-24 00:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 7504.
  • Revision ID: jelmer@jelmer.uk-20200524003950-bbc545r76vc5yajg
Add github action.

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