~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_rio.py

Turn completion assertions into separate methods.

Many common assertions used to be expressed as arguments to the complete
method.  This makes the checks more explicit, and the code easier to read.

Show diffs side-by-side

added added

removed removed

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