~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_rio.py

- refactor handling of short option names

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by 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.tests import TestCaseInTempDir, TestCase
31
 
from bzrlib.rio import (RioWriter, Stanza, read_stanza, read_stanzas, rio_file,
32
 
                        RioReader)
33
 
 
34
 
 
35
 
class TestRio(TestCase):
36
 
 
37
 
    def test_stanza(self):
38
 
        """Construct rio stanza in memory"""
39
 
        s = Stanza(number='42', name="fred")
40
 
        self.assertTrue('number' in s)
41
 
        self.assertFalse('color' in s)
42
 
        self.assertFalse('42' in s)
43
 
        self.assertEquals(list(s.iter_pairs()),
44
 
                [('name', 'fred'), ('number', '42')])
45
 
        self.assertEquals(s.get('number'), '42')
46
 
        self.assertEquals(s.get('name'), 'fred')
47
 
 
48
 
    def test_value_checks(self):
49
 
        """rio checks types on construction"""
50
 
        # these aren't enforced at construction time
51
 
        ## self.assertRaises(ValueError,
52
 
        ##        Stanza, complex=42 + 3j)
53
 
        ## self.assertRaises(ValueError, 
54
 
        ##        Stanza, several=range(10))
55
 
 
56
 
    def test_empty_value(self):
57
 
        """Serialize stanza with empty field"""
58
 
        s = Stanza(empty='')
59
 
        self.assertEqualDiff(s.to_string(),
60
 
                "empty: \n")
61
 
 
62
 
    def test_to_lines(self):
63
 
        """Write simple rio stanza to string"""
64
 
        s = Stanza(number='42', name='fred')
65
 
        self.assertEquals(list(s.to_lines()),
66
 
                ['name: fred\n',
67
 
                 'number: 42\n'])
68
 
 
69
 
    def test_as_dict(self):
70
 
        """Convert rio Stanza to dictionary"""
71
 
        s = Stanza(number='42', name='fred')
72
 
        sd = s.as_dict()
73
 
        self.assertEquals(sd, dict(number='42', name='fred'))
74
 
 
75
 
    def test_to_file(self):
76
 
        """Write rio to file"""
77
 
        tmpf = TemporaryFile()
78
 
        s = Stanza(a_thing='something with "quotes like \\"this\\""', number='42', name='fred')
79
 
        s.write(tmpf)
80
 
        tmpf.seek(0)
81
 
        self.assertEqualDiff(tmpf.read(), r'''
82
 
a_thing: something with "quotes like \"this\""
83
 
name: fred
84
 
number: 42
85
 
'''[1:])
86
 
 
87
 
    def test_multiline_string(self):
88
 
        tmpf = TemporaryFile()
89
 
        s = Stanza(motto="war is peace\nfreedom is slavery\nignorance is strength")
90
 
        s.write(tmpf)
91
 
        tmpf.seek(0)
92
 
        self.assertEqualDiff(tmpf.read(), '''\
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.assertEquals(s, s2)
100
 
 
101
 
    def test_read_stanza(self):
102
 
        """Load stanza from string"""
103
 
        lines = """\
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.assertEqualDiff(s.get('revision'), 'mbp@sourcefrog.net-123-abc')
112
 
        self.assertEquals(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.assertEquals(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.assertEquals(s, s2)
127
 
        self.assertEquals(s.get_all('a'), map(str, [10, 100, 1000]))
128
 
        self.assertEquals(s.get_all('b'), map(str, [20, 200, 2000]))
129
 
 
130
 
    def test_backslash(self):
131
 
        s = Stanza(q='\\')
132
 
        t = s.to_string()
133
 
        self.assertEqualDiff(t, 'q: \\\n')
134
 
        s2 = read_stanza(s.to_lines())
135
 
        self.assertEquals(s, s2)
136
 
 
137
 
    def test_blank_line(self):
138
 
        s = Stanza(none='', one='\n', two='\n\n')
139
 
        self.assertEqualDiff(s.to_string(), """\
140
 
none: 
141
 
one: 
142
 
\t
143
 
two: 
144
 
\t
145
 
\t
146
 
""")
147
 
        s2 = read_stanza(s.to_lines())
148
 
        self.assertEquals(s, s2)
149
 
 
150
 
    def test_whitespace_value(self):
151
 
        s = Stanza(space=' ', tabs='\t\t\t', combo='\n\t\t\n')
152
 
        self.assertEqualDiff(s.to_string(), """\
153
 
combo: 
154
 
\t\t\t
155
 
\t
156
 
space:  
157
 
tabs: \t\t\t
158
 
""")
159
 
        s2 = read_stanza(s.to_lines())
160
 
        self.assertEquals(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.assertEquals(s, s2)
177
 
        # apparent bug in read_stanza
178
 
        # s3 = read_stanza(self.stanzas_to_str([s]))
179
 
        # self.assertEquals(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_iter(self):
188
 
        """Read several stanzas from file"""
189
 
        tmpf = TemporaryFile()
190
 
        tmpf.write("""\
191
 
version_header: 1
192
 
 
193
 
name: foo
194
 
val: 123
195
 
 
196
 
name: bar
197
 
val: 129319
198
 
""")
199
 
        tmpf.seek(0)
200
 
        reader = read_stanzas(tmpf)
201
 
        read_iter = iter(reader)
202
 
        stuff = list(reader)
203
 
        self.assertEqual(stuff, 
204
 
                [ Stanza(version_header='1'),
205
 
                  Stanza(name="foo", val='123'),
206
 
                  Stanza(name="bar", val='129319'), ])
207
 
 
208
 
    def test_read_several(self):
209
 
        """Read several stanzas from file"""
210
 
        tmpf = TemporaryFile()
211
 
        tmpf.write("""\
212
 
version_header: 1
213
 
 
214
 
name: foo
215
 
val: 123
216
 
 
217
 
name: quoted
218
 
address:   "Willowglen"
219
 
\t  42 Wallaby Way
220
 
\t  Sydney
221
 
 
222
 
name: bar
223
 
val: 129319
224
 
""")
225
 
        tmpf.seek(0)
226
 
        s = read_stanza(tmpf)
227
 
        self.assertEquals(s, Stanza(version_header='1'))
228
 
        s = read_stanza(tmpf)
229
 
        self.assertEquals(s, Stanza(name="foo", val='123'))
230
 
        s = read_stanza(tmpf)
231
 
        self.assertEqualDiff(s.get('name'), 'quoted')
232
 
        self.assertEqualDiff(s.get('address'), '  "Willowglen"\n  42 Wallaby Way\n  Sydney')
233
 
        s = read_stanza(tmpf)
234
 
        self.assertEquals(s, Stanza(name="bar", val='129319'))
235
 
        s = read_stanza(tmpf)
236
 
        self.assertEquals(s, None)
237
 
        self.check_rio_file(tmpf)
238
 
 
239
 
    def check_rio_file(self, real_file):
240
 
        real_file.seek(0)
241
 
        read_write = rio_file(RioReader(real_file)).read()
242
 
        real_file.seek(0)
243
 
        self.assertEquals(read_write, real_file.read())
244
 
 
245
 
    @staticmethod
246
 
    def stanzas_to_str(stanzas):
247
 
        return rio_file(stanzas).read()
248
 
 
249
 
    def rio_file_stanzas(self, stanzas):
250
 
        new_stanzas = list(RioReader(rio_file(stanzas)))
251
 
        self.assertEqual(new_stanzas, stanzas)
252
 
 
253
 
    def test_tricky_quoted(self):
254
 
        tmpf = TemporaryFile()
255
 
        tmpf.write('''\
256
 
s: "one"
257
 
 
258
 
s: 
259
 
\t"one"
260
 
\t
261
 
 
262
 
s: "
263
 
 
264
 
s: ""
265
 
 
266
 
s: """
267
 
 
268
 
s: 
269
 
\t
270
 
 
271
 
s: \\
272
 
 
273
 
s: 
274
 
\t\\
275
 
\t\\\\
276
 
\t
277
 
 
278
 
s: word\\
279
 
 
280
 
s: quote"
281
 
 
282
 
s: backslashes\\\\\\
283
 
 
284
 
s: both\\\"
285
 
 
286
 
''')
287
 
        tmpf.seek(0)
288
 
        expected_vals = ['"one"',
289
 
            '\n"one"\n',
290
 
            '"',
291
 
            '""',
292
 
            '"""',
293
 
            '\n',
294
 
            '\\',
295
 
            '\n\\\n\\\\\n',
296
 
            'word\\',
297
 
            'quote\"',
298
 
            'backslashes\\\\\\',
299
 
            'both\\\"',
300
 
            ]
301
 
        for expected in expected_vals:
302
 
            stanza = read_stanza(tmpf)
303
 
            self.rio_file_stanzas([stanza])
304
 
            self.assertEquals(len(stanza), 1)
305
 
            self.assertEqualDiff(stanza.get('s'), expected)
306
 
 
307
 
    def test_write_empty_stanza(self):
308
 
        """Write empty stanza"""
309
 
        l = list(Stanza().to_lines())
310
 
        self.assertEquals(l, [])
311
 
 
312
 
    def test_rio_raises_type_error(self):
313
 
        """TypeError on adding invalid type to Stanza"""
314
 
        s = Stanza()
315
 
        self.assertRaises(TypeError, s.add, 'foo', {})
316
 
 
317
 
    def test_rio_raises_type_error_key(self):
318
 
        """TypeError on adding invalid type to Stanza"""
319
 
        s = Stanza()
320
 
        self.assertRaises(TypeError, s.add, 10, {})
321
 
 
322
 
    def test_rio_unicode(self):
323
 
        # intentionally use cStringIO which doesn't accomodate unencoded unicode objects
324
 
        sio = cStringIO.StringIO()
325
 
        uni_data = u'\N{KATAKANA LETTER O}'
326
 
        s = Stanza(foo=uni_data)
327
 
        self.assertEquals(s.get('foo'), uni_data)
328
 
        raw_lines = s.to_lines()
329
 
        self.assertEquals(raw_lines,
330
 
                ['foo: ' + uni_data.encode('utf-8') + '\n'])
331
 
        new_s = read_stanza(raw_lines)
332
 
        self.assertEquals(new_s.get('foo'), uni_data)
333