1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
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
17
"""Tests for rio serialization
19
A simple, reproducible structured IO format.
21
rio itself works in Unicode strings. It is typically encoded to UTF-8,
22
but this depends on the transport.
28
from tempfile import TemporaryFile
33
from bzrlib.tests import TestCaseInTempDir, TestCase
34
from bzrlib.rio import (RioWriter, Stanza, read_stanza, read_stanzas, rio_file,
38
class TestRio(TestCase):
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')
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))
59
def test_empty_value(self):
60
"""Serialize stanza with empty field"""
62
self.assertEqualDiff(s.to_string(),
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()),
72
def test_as_dict(self):
73
"""Convert rio Stanza to dictionary"""
74
s = Stanza(number='42', name='fred')
76
self.assertEquals(sd, dict(number='42', name='fred'))
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')
84
self.assertEqualDiff(tmpf.read(), r'''
85
a_thing: something with "quotes like \"this\""
90
def test_multiline_string(self):
91
tmpf = TemporaryFile()
92
s = Stanza(motto="war is peace\nfreedom is slavery\nignorance is strength")
95
self.assertEqualDiff(tmpf.read(), '''\
98
\tignorance is strength
101
s2 = read_stanza(tmpf)
102
self.assertEquals(s, s2)
104
def test_read_stanza(self):
105
"""Load stanza from string"""
107
revision: mbp@sourcefrog.net-123-abc
108
timestamp: 1130653962
110
committer: Martin Pool <mbp@test.sourcefrog.net>
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)
122
def test_repeated_field(self):
123
"""Repeated field in rio"""
125
for k, v in [('a', '10'), ('b', '20'), ('a', '100'), ('b', '200'),
126
('a', '1000'), ('b', '2000')]:
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]))
133
def test_backslash(self):
136
self.assertEqualDiff(t, 'q: \\\n')
137
s2 = read_stanza(s.to_lines())
138
self.assertEquals(s, s2)
140
def test_blank_line(self):
141
s = Stanza(none='', one='\n', two='\n\n')
142
self.assertEqualDiff(s.to_string(), """\
150
s2 = read_stanza(s.to_lines())
151
self.assertEquals(s, s2)
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(), """\
162
s2 = read_stanza(s.to_lines())
163
self.assertEquals(s, s2)
164
self.rio_file_stanzas([s])
166
def test_quoted(self):
167
"""rio quoted string cases"""
168
s = Stanza(q1='"hello"',
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)
184
def test_read_empty(self):
185
"""Detect end of rio file"""
187
self.assertEqual(s, None)
188
self.assertTrue(s is None)
190
def test_read_iter(self):
191
"""Read several stanzas from file"""
192
tmpf = TemporaryFile()
203
reader = read_stanzas(tmpf)
204
read_iter = iter(reader)
206
self.assertEqual(stuff,
207
[ Stanza(version_header='1'),
208
Stanza(name="foo", val='123'),
209
Stanza(name="bar", val='129319'), ])
211
def test_read_several(self):
212
"""Read several stanzas from file"""
213
tmpf = TemporaryFile()
221
address: "Willowglen"
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)
242
def check_rio_file(self, real_file):
244
read_write = rio_file(RioReader(real_file)).read()
246
self.assertEquals(read_write, real_file.read())
249
def stanzas_to_str(stanzas):
250
return rio_file(stanzas).read()
252
def rio_file_stanzas(self, stanzas):
253
new_stanzas = list(RioReader(rio_file(stanzas)))
254
self.assertEqual(new_stanzas, stanzas)
256
def test_tricky_quoted(self):
257
tmpf = TemporaryFile()
291
expected_vals = ['"one"',
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)
310
def test_write_empty_stanza(self):
311
"""Write empty stanza"""
312
l = list(Stanza().to_lines())
313
self.assertEquals(l, [])
315
def test_rio_raises_type_error(self):
316
"""TypeError on adding invalid type to Stanza"""
318
self.assertRaises(TypeError, s.add, 'foo', {})
320
def test_rio_raises_type_error_key(self):
321
"""TypeError on adding invalid type to Stanza"""
323
self.assertRaises(TypeError, s.add, 10, {})
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)
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'))
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',
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'))