1
# Copyright (C) 2007 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 bzrlib.pack."""
20
from cStringIO import StringIO
22
from bzrlib import pack, errors, tests
25
class TestContainerWriter(tests.TestCase):
27
def test_construct(self):
28
"""Test constructing a ContainerWriter.
30
This uses None as the output stream to show that the constructor doesn't
31
try to use the output stream.
33
writer = pack.ContainerWriter(None)
36
"""The begin() method writes the container format marker line."""
38
writer = pack.ContainerWriter(output.write)
40
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
44
"""The end() method writes an End Marker record."""
46
writer = pack.ContainerWriter(output.write)
49
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
52
def test_add_bytes_record_no_name(self):
53
"""Add a bytes record with no name."""
55
writer = pack.ContainerWriter(output.write)
57
offset, length = writer.add_bytes_record('abc', names=[])
58
self.assertEqual((42, 7), (offset, length))
59
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
62
def test_add_bytes_record_one_name(self):
63
"""Add a bytes record with one name."""
65
writer = pack.ContainerWriter(output.write)
67
offset, length = writer.add_bytes_record('abc', names=['name1'])
68
self.assertEqual((42, 13), (offset, length))
70
'Bazaar pack format 1 (introduced in 0.18)\n'
74
def test_add_bytes_record_two_names(self):
75
"""Add a bytes record with two names."""
77
writer = pack.ContainerWriter(output.write)
79
offset, length = writer.add_bytes_record('abc', names=['name1', 'name2'])
80
self.assertEqual((42, 19), (offset, length))
82
'Bazaar pack format 1 (introduced in 0.18)\n'
83
'B3\nname1\nname2\n\nabc',
86
def test_add_second_bytes_record_gets_higher_offset(self):
88
writer = pack.ContainerWriter(output.write)
90
writer.add_bytes_record('abc', names=[])
91
offset, length = writer.add_bytes_record('abc', names=[])
92
self.assertEqual((49, 7), (offset, length))
94
'Bazaar pack format 1 (introduced in 0.18)\n'
99
def test_add_bytes_record_invalid_name(self):
100
"""Adding a Bytes record with a name with whitespace in it raises
104
writer = pack.ContainerWriter(output.write)
107
errors.InvalidRecordError,
108
writer.add_bytes_record, 'abc', names=['bad name'])
111
class TestContainerReader(tests.TestCase):
113
def get_reader_for(self, bytes):
114
stream = StringIO(bytes)
115
reader = pack.ContainerReader(stream)
118
def test_construct(self):
119
"""Test constructing a ContainerReader.
121
This uses None as the output stream to show that the constructor doesn't
122
try to use the input stream.
124
reader = pack.ContainerReader(None)
126
def test_empty_container(self):
127
"""Read an empty container."""
128
reader = self.get_reader_for(
129
"Bazaar pack format 1 (introduced in 0.18)\nE")
130
self.assertEqual([], list(reader.iter_records()))
132
def test_unknown_format(self):
133
"""Unrecognised container formats raise UnknownContainerFormatError."""
134
reader = self.get_reader_for("unknown format\n")
136
errors.UnknownContainerFormatError, reader.iter_records)
138
def test_unexpected_end_of_container(self):
139
"""Containers that don't end with an End Marker record should cause
140
UnexpectedEndOfContainerError to be raised.
142
reader = self.get_reader_for(
143
"Bazaar pack format 1 (introduced in 0.18)\n")
144
iterator = reader.iter_records()
146
errors.UnexpectedEndOfContainerError, iterator.next)
148
def test_unknown_record_type(self):
149
"""Unknown record types cause UnknownRecordTypeError to be raised."""
150
reader = self.get_reader_for(
151
"Bazaar pack format 1 (introduced in 0.18)\nX")
152
iterator = reader.iter_records()
154
errors.UnknownRecordTypeError, iterator.next)
156
def test_container_with_one_unnamed_record(self):
157
"""Read a container with one Bytes record.
159
Parsing Bytes records is more thoroughly exercised by
160
TestBytesRecordReader. This test is here to ensure that
161
ContainerReader's integration with BytesRecordReader is working.
163
reader = self.get_reader_for(
164
"Bazaar pack format 1 (introduced in 0.18)\nB5\n\naaaaaE")
165
expected_records = [([], 'aaaaa')]
168
[(names, read_bytes(None))
169
for (names, read_bytes) in reader.iter_records()])
171
def test_validate_empty_container(self):
172
"""validate does not raise an error for a container with no records."""
173
reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
174
# No exception raised
177
def test_validate_non_empty_valid_container(self):
178
"""validate does not raise an error for a container with a valid record.
180
reader = self.get_reader_for(
181
"Bazaar pack format 1 (introduced in 0.18)\nB3\nname\n\nabcE")
182
# No exception raised
185
def test_validate_bad_format(self):
186
"""validate raises an error for unrecognised format strings.
188
It may raise either UnexpectedEndOfContainerError or
189
UnknownContainerFormatError, depending on exactly what the string is.
191
inputs = ["", "x", "Bazaar pack format 1 (introduced in 0.18)", "bad\n"]
193
reader = self.get_reader_for(input)
195
(errors.UnexpectedEndOfContainerError,
196
errors.UnknownContainerFormatError),
199
def test_validate_bad_record_marker(self):
200
"""validate raises UnknownRecordTypeError for unrecognised record
203
reader = self.get_reader_for(
204
"Bazaar pack format 1 (introduced in 0.18)\nX")
205
self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
207
def test_validate_data_after_end_marker(self):
208
"""validate raises ContainerHasExcessDataError if there are any bytes
209
after the end of the container.
211
reader = self.get_reader_for(
212
"Bazaar pack format 1 (introduced in 0.18)\nEcrud")
214
errors.ContainerHasExcessDataError, reader.validate)
216
def test_validate_no_end_marker(self):
217
"""validate raises UnexpectedEndOfContainerError if there's no end of
218
container marker, even if the container up to this point has been valid.
220
reader = self.get_reader_for(
221
"Bazaar pack format 1 (introduced in 0.18)\n")
223
errors.UnexpectedEndOfContainerError, reader.validate)
225
def test_validate_duplicate_name(self):
226
"""validate raises DuplicateRecordNameError if the same name occurs
227
multiple times in the container.
229
reader = self.get_reader_for(
230
"Bazaar pack format 1 (introduced in 0.18)\n"
234
self.assertRaises(errors.DuplicateRecordNameError, reader.validate)
236
def test_validate_undecodeable_name(self):
237
"""Names that aren't valid UTF-8 cause validate to fail."""
238
reader = self.get_reader_for(
239
"Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
240
self.assertRaises(errors.InvalidRecordError, reader.validate)
243
class TestBytesRecordReader(tests.TestCase):
244
"""Tests for reading and validating Bytes records with BytesRecordReader."""
246
def get_reader_for(self, bytes):
247
stream = StringIO(bytes)
248
reader = pack.BytesRecordReader(stream)
251
def test_record_with_no_name(self):
252
"""Reading a Bytes record with no name returns an empty list of
255
reader = self.get_reader_for("5\n\naaaaa")
256
names, get_bytes = reader.read()
257
self.assertEqual([], names)
258
self.assertEqual('aaaaa', get_bytes(None))
260
def test_record_with_one_name(self):
261
"""Reading a Bytes record with one name returns a list of just that
264
reader = self.get_reader_for("5\nname1\n\naaaaa")
265
names, get_bytes = reader.read()
266
self.assertEqual(['name1'], names)
267
self.assertEqual('aaaaa', get_bytes(None))
269
def test_record_with_two_names(self):
270
"""Reading a Bytes record with two names returns a list of both names.
272
reader = self.get_reader_for("5\nname1\nname2\n\naaaaa")
273
names, get_bytes = reader.read()
274
self.assertEqual(['name1', 'name2'], names)
275
self.assertEqual('aaaaa', get_bytes(None))
277
def test_invalid_length(self):
278
"""If the length-prefix is not a number, parsing raises
281
reader = self.get_reader_for("not a number\n")
282
self.assertRaises(errors.InvalidRecordError, reader.read)
284
def test_early_eof(self):
285
"""Tests for premature EOF occuring during parsing Bytes records with
288
A incomplete container might be interrupted at any point. The
289
BytesRecordReader needs to cope with the input stream running out no
290
matter where it is in the parsing process.
292
In all cases, UnexpectedEndOfContainerError should be raised.
294
complete_record = "6\nname\n\nabcdef"
295
for count in range(0, len(complete_record)):
296
incomplete_record = complete_record[:count]
297
reader = self.get_reader_for(incomplete_record)
298
# We don't use assertRaises to make diagnosing failures easier
299
# (assertRaises doesn't allow a custom failure message).
301
names, read_bytes = reader.read()
303
except errors.UnexpectedEndOfContainerError:
307
"UnexpectedEndOfContainerError not raised when parsing %r"
308
% (incomplete_record,))
310
def test_initial_eof(self):
311
"""EOF before any bytes read at all."""
312
reader = self.get_reader_for("")
313
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
315
def test_eof_after_length(self):
316
"""EOF after reading the length and before reading name(s)."""
317
reader = self.get_reader_for("123\n")
318
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
320
def test_eof_during_name(self):
321
"""EOF during reading a name."""
322
reader = self.get_reader_for("123\nname")
323
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
325
def test_read_invalid_name_whitespace(self):
326
"""Names must have no whitespace."""
327
# A name with a space.
328
reader = self.get_reader_for("0\nbad name\n\n")
329
self.assertRaises(errors.InvalidRecordError, reader.read)
332
reader = self.get_reader_for("0\nbad\tname\n\n")
333
self.assertRaises(errors.InvalidRecordError, reader.read)
335
# A name with a vertical tab.
336
reader = self.get_reader_for("0\nbad\vname\n\n")
337
self.assertRaises(errors.InvalidRecordError, reader.read)
339
def test_validate_whitespace_in_name(self):
340
"""Names must have no whitespace."""
341
reader = self.get_reader_for("0\nbad name\n\n")
342
self.assertRaises(errors.InvalidRecordError, reader.validate)
344
def test_validate_interrupted_prelude(self):
345
"""EOF during reading a record's prelude causes validate to fail."""
346
reader = self.get_reader_for("")
348
errors.UnexpectedEndOfContainerError, reader.validate)
350
def test_validate_interrupted_body(self):
351
"""EOF during reading a record's body causes validate to fail."""
352
reader = self.get_reader_for("1\n\n")
354
errors.UnexpectedEndOfContainerError, reader.validate)
356
def test_validate_unparseable_length(self):
357
"""An unparseable record length causes validate to fail."""
358
reader = self.get_reader_for("\n\n")
360
errors.InvalidRecordError, reader.validate)
362
def test_validate_undecodeable_name(self):
363
"""Names that aren't valid UTF-8 cause validate to fail."""
364
reader = self.get_reader_for("0\n\xcc\n\n")
365
self.assertRaises(errors.InvalidRecordError, reader.validate)
367
def test_read_max_length(self):
368
"""If the max_length passed to the callable returned by read is not
369
None, then no more than that many bytes will be read.
371
reader = self.get_reader_for("6\n\nabcdef")
372
names, get_bytes = reader.read()
373
self.assertEqual('abc', get_bytes(3))
375
def test_read_no_max_length(self):
376
"""If the max_length passed to the callable returned by read is None,
377
then all the bytes in the record will be read.
379
reader = self.get_reader_for("6\n\nabcdef")
380
names, get_bytes = reader.read()
381
self.assertEqual('abcdef', get_bytes(None))
383
def test_repeated_read_calls(self):
384
"""Repeated calls to the callable returned from BytesRecordReader.read
385
will not read beyond the end of the record.
387
reader = self.get_reader_for("6\n\nabcdefB3\nnext-record\nXXX")
388
names, get_bytes = reader.read()
389
self.assertEqual('abcdef', get_bytes(None))
390
self.assertEqual('', get_bytes(None))
391
self.assertEqual('', get_bytes(99))
394
class TestMakeReadvReader(tests.TestCaseWithTransport):
396
def test_read_skipping_records(self):
397
pack_data = StringIO()
398
writer = pack.ContainerWriter(pack_data.write)
401
memos.append(writer.add_bytes_record('abc', names=[]))
402
memos.append(writer.add_bytes_record('def', names=['name1']))
403
memos.append(writer.add_bytes_record('ghi', names=['name2']))
404
memos.append(writer.add_bytes_record('jkl', names=[]))
406
transport = self.get_transport()
407
transport.put_bytes('mypack', pack_data.getvalue())
408
requested_records = [memos[0], memos[2]]
409
reader = pack.make_readv_reader(transport, 'mypack', requested_records)
411
for names, reader_func in reader.iter_records():
412
result.append((names, reader_func(None)))
413
self.assertEqual([([], 'abc'), (['name2'], 'ghi')], result)
416
class TestReadvFile(tests.TestCaseWithTransport):
417
"""Tests of the ReadVFile class.
419
Error cases are deliberately undefined: this code adapts the underlying
420
transport interface to a single 'streaming read' interface as
421
ContainerReader needs.
424
def test_read_bytes(self):
425
"""Test reading of both single bytes and all bytes in a hunk."""
426
transport = self.get_transport()
427
transport.put_bytes('sample', '0123456789')
428
f = pack.ReadVFile(transport.readv('sample', [(0,1), (1,2), (4,1), (6,2)]))
430
results.append(f.read(1))
431
results.append(f.read(2))
432
results.append(f.read(1))
433
results.append(f.read(1))
434
results.append(f.read(1))
435
self.assertEqual(['0', '12', '4', '6', '7'], results)
437
def test_readline(self):
438
"""Test using readline() as ContainerReader does.
440
This is always within a readv hunk, never across it.
442
transport = self.get_transport()
443
transport.put_bytes('sample', '0\n2\n4\n')
444
f = pack.ReadVFile(transport.readv('sample', [(0,2), (2,4)]))
446
results.append(f.readline())
447
results.append(f.readline())
448
results.append(f.readline())
449
self.assertEqual(['0\n', '2\n', '4\n'], results)
451
def test_readline_and_read(self):
452
"""Test exercising one byte reads, readline, and then read again."""
453
transport = self.get_transport()
454
transport.put_bytes('sample', '0\n2\n4\n')
455
f = pack.ReadVFile(transport.readv('sample', [(0,6)]))
457
results.append(f.read(1))
458
results.append(f.readline())
459
results.append(f.read(4))
460
self.assertEqual(['0', '\n', '2\n4\n'], results)