~bzr-pqm/bzr/bzr.dev

1553.5.7 by Martin Pool
rio.Stanza.add should raise TypeError on invalid types.
1
# Copyright (C) 2005, 2006 by Canonical Ltd
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
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
1553.5.32 by Martin Pool
rio files are always externalized in utf-8. test this.
25
import cStringIO
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
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
32
33
34
class TestRio(TestCase):
35
36
    def test_stanza(self):
37
        """Construct rio stanza in memory"""
1185.47.2 by Martin Pool
Finish rio format and tests.
38
        s = Stanza(number='42', name="fred")
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
39
        self.assertTrue('number' in s)
40
        self.assertFalse('color' in s)
1185.47.2 by Martin Pool
Finish rio format and tests.
41
        self.assertFalse('42' in s)
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
42
        self.assertEquals(list(s.iter_pairs()),
1185.47.2 by Martin Pool
Finish rio format and tests.
43
                [('name', 'fred'), ('number', '42')])
44
        self.assertEquals(s.get('number'), '42')
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
45
        self.assertEquals(s.get('name'), 'fred')
46
47
    def test_value_checks(self):
48
        """rio checks types on construction"""
49
        # these aren't enforced at construction time
50
        ## self.assertRaises(ValueError,
51
        ##        Stanza, complex=42 + 3j)
52
        ## self.assertRaises(ValueError, 
53
        ##        Stanza, several=range(10))
54
55
    def test_empty_value(self):
56
        """Serialize stanza with empty field"""
57
        s = Stanza(empty='')
58
        self.assertEqualDiff(s.to_string(),
59
                "empty: \n")
60
61
    def test_to_lines(self):
62
        """Write simple rio stanza to string"""
1185.47.2 by Martin Pool
Finish rio format and tests.
63
        s = Stanza(number='42', name='fred')
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
64
        self.assertEquals(list(s.to_lines()),
65
                ['name: fred\n',
66
                 'number: 42\n'])
67
1553.5.8 by Martin Pool
New Rio.as_dict method
68
    def test_as_dict(self):
69
        """Convert rio Stanza to dictionary"""
70
        s = Stanza(number='42', name='fred')
71
        sd = s.as_dict()
72
        self.assertEquals(sd, dict(number='42', name='fred'))
73
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
74
    def test_to_file(self):
75
        """Write rio to file"""
76
        tmpf = TemporaryFile()
1185.47.2 by Martin Pool
Finish rio format and tests.
77
        s = Stanza(a_thing='something with "quotes like \\"this\\""', number='42', name='fred')
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
78
        s.write(tmpf)
79
        tmpf.seek(0)
80
        self.assertEqualDiff(tmpf.read(), r'''
81
a_thing: something with "quotes like \"this\""
82
name: fred
83
number: 42
84
'''[1:])
85
86
    def test_multiline_string(self):
87
        tmpf = TemporaryFile()
88
        s = Stanza(motto="war is peace\nfreedom is slavery\nignorance is strength")
89
        s.write(tmpf)
90
        tmpf.seek(0)
91
        self.assertEqualDiff(tmpf.read(), '''\
92
motto: war is peace
93
\tfreedom is slavery
94
\tignorance is strength
95
''')
96
        tmpf.seek(0)
97
        s2 = read_stanza(tmpf)
98
        self.assertEquals(s, s2)
99
100
    def test_read_stanza(self):
101
        """Load stanza from string"""
102
        lines = """\
103
revision: mbp@sourcefrog.net-123-abc
104
timestamp: 1130653962
105
timezone: 36000
106
committer: Martin Pool <mbp@test.sourcefrog.net>
107
""".splitlines(True)
108
        s = read_stanza(lines)
109
        self.assertTrue('revision' in s)
110
        self.assertEqualDiff(s.get('revision'), 'mbp@sourcefrog.net-123-abc')
111
        self.assertEquals(list(s.iter_pairs()),
112
                [('revision', 'mbp@sourcefrog.net-123-abc'),
113
                 ('timestamp', '1130653962'),
114
                 ('timezone', '36000'),
115
                 ('committer', "Martin Pool <mbp@test.sourcefrog.net>")])
116
        self.assertEquals(len(s), 4)
117
118
    def test_repeated_field(self):
119
        """Repeated field in rio"""
120
        s = Stanza()
121
        for k, v in [('a', '10'), ('b', '20'), ('a', '100'), ('b', '200'), 
122
                     ('a', '1000'), ('b', '2000')]:
123
            s.add(k, v)
124
        s2 = read_stanza(s.to_lines())
125
        self.assertEquals(s, s2)
126
        self.assertEquals(s.get_all('a'), map(str, [10, 100, 1000]))
127
        self.assertEquals(s.get_all('b'), map(str, [20, 200, 2000]))
128
1185.47.2 by Martin Pool
Finish rio format and tests.
129
    def test_backslash(self):
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
130
        s = Stanza(q='\\')
131
        t = s.to_string()
1185.47.2 by Martin Pool
Finish rio format and tests.
132
        self.assertEqualDiff(t, 'q: \\\n')
133
        s2 = read_stanza(s.to_lines())
134
        self.assertEquals(s, s2)
135
136
    def test_blank_line(self):
137
        s = Stanza(none='', one='\n', two='\n\n')
138
        self.assertEqualDiff(s.to_string(), """\
139
none: 
140
one: 
141
\t
142
two: 
143
\t
144
\t
145
""")
146
        s2 = read_stanza(s.to_lines())
147
        self.assertEquals(s, s2)
148
149
    def test_whitespace_value(self):
150
        s = Stanza(space=' ', tabs='\t\t\t', combo='\n\t\t\n')
151
        self.assertEqualDiff(s.to_string(), """\
152
combo: 
153
\t\t\t
154
\t
155
space:  
156
tabs: \t\t\t
157
""")
158
        s2 = read_stanza(s.to_lines())
159
        self.assertEquals(s, s2)
160
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
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.assertEquals(s, s2)
175
176
    def test_read_empty(self):
177
        """Detect end of rio file"""
178
        s = read_stanza([])
179
        self.assertEqual(s, None)
180
        self.assertTrue(s is None)
181
        
182
    def test_read_iter(self):
183
        """Read several stanzas from file"""
184
        tmpf = TemporaryFile()
185
        tmpf.write("""\
1185.47.2 by Martin Pool
Finish rio format and tests.
186
version_header: 1
187
188
name: foo
189
val: 123
190
191
name: bar
192
val: 129319
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
193
""")
194
        tmpf.seek(0)
195
        reader = read_stanzas(tmpf)
196
        read_iter = iter(reader)
197
        stuff = list(reader)
198
        self.assertEqual(stuff, 
1185.47.2 by Martin Pool
Finish rio format and tests.
199
                [ Stanza(version_header='1'),
200
                  Stanza(name="foo", val='123'),
201
                  Stanza(name="bar", val='129319'), ])
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
202
203
    def test_read_several(self):
204
        """Read several stanzas from file"""
205
        tmpf = TemporaryFile()
206
        tmpf.write("""\
1185.47.2 by Martin Pool
Finish rio format and tests.
207
version_header: 1
208
209
name: foo
210
val: 123
211
212
name: quoted
213
address:   "Willowglen"
214
\t  42 Wallaby Way
215
\t  Sydney
216
217
name: bar
218
val: 129319
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
219
""")
220
        tmpf.seek(0)
221
        s = read_stanza(tmpf)
1185.47.2 by Martin Pool
Finish rio format and tests.
222
        self.assertEquals(s, Stanza(version_header='1'))
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
223
        s = read_stanza(tmpf)
1185.47.2 by Martin Pool
Finish rio format and tests.
224
        self.assertEquals(s, Stanza(name="foo", val='123'))
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
225
        s = read_stanza(tmpf)
226
        self.assertEqualDiff(s.get('name'), 'quoted')
227
        self.assertEqualDiff(s.get('address'), '  "Willowglen"\n  42 Wallaby Way\n  Sydney')
228
        s = read_stanza(tmpf)
1185.47.2 by Martin Pool
Finish rio format and tests.
229
        self.assertEquals(s, Stanza(name="bar", val='129319'))
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
230
        s = read_stanza(tmpf)
231
        self.assertEquals(s, None)
232
233
    def test_tricky_quoted(self):
234
        tmpf = TemporaryFile()
1185.47.2 by Martin Pool
Finish rio format and tests.
235
        tmpf.write('''\
236
s: "one"
237
238
s: 
239
\t"one"
240
\t
241
242
s: "
243
244
s: ""
245
246
s: """
247
248
s: 
249
\t
250
251
s: \\
252
253
s: 
254
\t\\
255
\t\\\\
256
\t
257
258
s: word\\
259
260
s: quote"
261
262
s: backslashes\\\\\\
263
264
s: both\\\"
265
266
''')
1185.47.1 by Martin Pool
[broken] start converting basic_io to more rfc822-like format
267
        tmpf.seek(0)
268
        expected_vals = ['"one"',
269
            '\n"one"\n',
270
            '"',
271
            '""',
272
            '"""',
273
            '\n',
274
            '\\',
275
            '\n\\\n\\\\\n',
276
            'word\\',
277
            'quote\"',
278
            'backslashes\\\\\\',
279
            'both\\\"',
280
            ]
281
        for expected in expected_vals:
282
            stanza = read_stanza(tmpf)
283
            self.assertEquals(len(stanza), 1)
284
            self.assertEqualDiff(stanza.get('s'), expected)
285
286
    def test_write_empty_stanza(self):
287
        """Write empty stanza"""
288
        l = list(Stanza().to_lines())
289
        self.assertEquals(l, [])
1553.5.7 by Martin Pool
rio.Stanza.add should raise TypeError on invalid types.
290
291
    def test_rio_raises_type_error(self):
292
        """TypeError on adding invalid type to Stanza"""
293
        s = Stanza()
294
        self.assertRaises(TypeError, s.add, 'foo', {})
295
296
    def test_rio_raises_type_error_key(self):
297
        """TypeError on adding invalid type to Stanza"""
298
        s = Stanza()
299
        self.assertRaises(TypeError, s.add, 10, {})
1553.5.32 by Martin Pool
rio files are always externalized in utf-8. test this.
300
301
    def test_rio_unicode(self):
302
        # intentionally use cStringIO which doesn't accomodate unencoded unicode objects
303
        sio = cStringIO.StringIO()
304
        uni_data = u'\N{KATAKANA LETTER O}'
305
        s = Stanza(foo=uni_data)
306
        self.assertEquals(s.get('foo'), uni_data)
307
        raw_lines = s.to_lines()
308
        self.assertEquals(raw_lines,
309
                ['foo: ' + uni_data.encode('utf-8') + '\n'])
310
        new_s = read_stanza(raw_lines)
311
        self.assertEquals(new_s.get('foo'), uni_data)
312