~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_rio.py

merge merge tweaks from aaron, which includes latest .dev

Show diffs side-by-side

added added

removed removed

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