~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 18:47:00 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730184700-cfc979f8f530908d
setup.py: get version info from bzrlib

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 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 bzrlib.pack."""
18
 
 
19
 
 
20
 
from cStringIO import StringIO
21
 
 
22
 
from bzrlib import pack, errors, tests
23
 
 
24
 
 
25
 
class TestContainerSerialiser(tests.TestCase):
26
 
    """Tests for the ContainerSerialiser class."""
27
 
 
28
 
    def test_construct(self):
29
 
        """Test constructing a ContainerSerialiser."""
30
 
        pack.ContainerSerialiser()
31
 
 
32
 
    def test_begin(self):
33
 
        serialiser = pack.ContainerSerialiser()
34
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
35
 
                         serialiser.begin())
36
 
 
37
 
    def test_end(self):
38
 
        serialiser = pack.ContainerSerialiser()
39
 
        self.assertEqual('E', serialiser.end())
40
 
 
41
 
    def test_bytes_record_no_name(self):
42
 
        serialiser = pack.ContainerSerialiser()
43
 
        record = serialiser.bytes_record('bytes', [])
44
 
        self.assertEqual('B5\n\nbytes', record)
45
 
        
46
 
    def test_bytes_record_one_name_with_one_part(self):
47
 
        serialiser = pack.ContainerSerialiser()
48
 
        record = serialiser.bytes_record('bytes', [('name',)])
49
 
        self.assertEqual('B5\nname\n\nbytes', record)
50
 
        
51
 
    def test_bytes_record_one_name_with_two_parts(self):
52
 
        serialiser = pack.ContainerSerialiser()
53
 
        record = serialiser.bytes_record('bytes', [('part1', 'part2')])
54
 
        self.assertEqual('B5\npart1\x00part2\n\nbytes', record)
55
 
        
56
 
    def test_bytes_record_two_names(self):
57
 
        serialiser = pack.ContainerSerialiser()
58
 
        record = serialiser.bytes_record('bytes', [('name1',), ('name2',)])
59
 
        self.assertEqual('B5\nname1\nname2\n\nbytes', record)
60
 
 
61
 
    def test_bytes_record_whitespace_in_name_part(self):
62
 
        serialiser = pack.ContainerSerialiser()
63
 
        self.assertRaises(
64
 
            errors.InvalidRecordError,
65
 
            serialiser.bytes_record, 'bytes', [('bad name',)])
66
 
 
67
 
 
68
 
class TestContainerWriter(tests.TestCase):
69
 
 
70
 
    def setUp(self):
71
 
        self.output = StringIO()
72
 
        self.writer = pack.ContainerWriter(self.output.write)
73
 
 
74
 
    def assertOutput(self, expected_output):
75
 
        """Assert that the output of self.writer ContainerWriter is equal to
76
 
        expected_output.
77
 
        """
78
 
        self.assertEqual(expected_output, self.output.getvalue())
79
 
 
80
 
    def test_construct(self):
81
 
        """Test constructing a ContainerWriter.
82
 
        
83
 
        This uses None as the output stream to show that the constructor
84
 
        doesn't try to use the output stream.
85
 
        """
86
 
        writer = pack.ContainerWriter(None)
87
 
 
88
 
    def test_begin(self):
89
 
        """The begin() method writes the container format marker line."""
90
 
        self.writer.begin()
91
 
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\n')
92
 
 
93
 
    def test_zero_records_written_after_begin(self):
94
 
        """After begin is written, 0 records have been written."""
95
 
        self.writer.begin()
96
 
        self.assertEqual(0, self.writer.records_written)
97
 
 
98
 
    def test_end(self):
99
 
        """The end() method writes an End Marker record."""
100
 
        self.writer.begin()
101
 
        self.writer.end()
102
 
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
103
 
 
104
 
    def test_empty_end_does_not_add_a_record_to_records_written(self):
105
 
        """The end() method does not count towards the records written."""
106
 
        self.writer.begin()
107
 
        self.writer.end()
108
 
        self.assertEqual(0, self.writer.records_written)
109
 
 
110
 
    def test_non_empty_end_does_not_add_a_record_to_records_written(self):
111
 
        """The end() method does not count towards the records written."""
112
 
        self.writer.begin()
113
 
        self.writer.add_bytes_record('foo', names=[])
114
 
        self.writer.end()
115
 
        self.assertEqual(1, self.writer.records_written)
116
 
 
117
 
    def test_add_bytes_record_no_name(self):
118
 
        """Add a bytes record with no name."""
119
 
        self.writer.begin()
120
 
        offset, length = self.writer.add_bytes_record('abc', names=[])
121
 
        self.assertEqual((42, 7), (offset, length))
122
 
        self.assertOutput(
123
 
            'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
124
 
 
125
 
    def test_add_bytes_record_one_name(self):
126
 
        """Add a bytes record with one name."""
127
 
        self.writer.begin()
128
 
        offset, length = self.writer.add_bytes_record(
129
 
            'abc', names=[('name1', )])
130
 
        self.assertEqual((42, 13), (offset, length))
131
 
        self.assertOutput(
132
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
133
 
            'B3\nname1\n\nabc')
134
 
 
135
 
    def test_add_bytes_record_two_names(self):
136
 
        """Add a bytes record with two names."""
137
 
        self.writer.begin()
138
 
        offset, length = self.writer.add_bytes_record(
139
 
            'abc', names=[('name1', ), ('name2', )])
140
 
        self.assertEqual((42, 19), (offset, length))
141
 
        self.assertOutput(
142
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
143
 
            'B3\nname1\nname2\n\nabc')
144
 
 
145
 
    def test_add_bytes_record_two_names(self):
146
 
        """Add a bytes record with two names."""
147
 
        self.writer.begin()
148
 
        offset, length = self.writer.add_bytes_record(
149
 
            'abc', names=[('name1', ), ('name2', )])
150
 
        self.assertEqual((42, 19), (offset, length))
151
 
        self.assertOutput(
152
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
153
 
            'B3\nname1\nname2\n\nabc')
154
 
 
155
 
    def test_add_bytes_record_two_element_name(self):
156
 
        """Add a bytes record with a two-element name."""
157
 
        self.writer.begin()
158
 
        offset, length = self.writer.add_bytes_record(
159
 
            'abc', names=[('name1', 'name2')])
160
 
        self.assertEqual((42, 19), (offset, length))
161
 
        self.assertOutput(
162
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
163
 
            'B3\nname1\x00name2\n\nabc')
164
 
 
165
 
    def test_add_second_bytes_record_gets_higher_offset(self):
166
 
        self.writer.begin()
167
 
        self.writer.add_bytes_record('abc', names=[])
168
 
        offset, length = self.writer.add_bytes_record('abc', names=[])
169
 
        self.assertEqual((49, 7), (offset, length))
170
 
        self.assertOutput(
171
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
172
 
            'B3\n\nabc'
173
 
            'B3\n\nabc')
174
 
 
175
 
    def test_add_bytes_record_invalid_name(self):
176
 
        """Adding a Bytes record with a name with whitespace in it raises
177
 
        InvalidRecordError.
178
 
        """
179
 
        self.writer.begin()
180
 
        self.assertRaises(
181
 
            errors.InvalidRecordError,
182
 
            self.writer.add_bytes_record, 'abc', names=[('bad name', )])
183
 
 
184
 
    def test_add_bytes_records_add_to_records_written(self):
185
 
        """Adding a Bytes record increments the records_written counter."""
186
 
        self.writer.begin()
187
 
        self.writer.add_bytes_record('foo', names=[])
188
 
        self.assertEqual(1, self.writer.records_written)
189
 
        self.writer.add_bytes_record('foo', names=[])
190
 
        self.assertEqual(2, self.writer.records_written)
191
 
 
192
 
 
193
 
class TestContainerReader(tests.TestCase):
194
 
    """Tests for the ContainerReader.
195
 
 
196
 
    The ContainerReader reads format 1 containers, so these tests explicitly
197
 
    test how it reacts to format 1 data.  If a new version of the format is
198
 
    added, then separate tests for that format should be added.
199
 
    """
200
 
 
201
 
    def get_reader_for(self, bytes):
202
 
        stream = StringIO(bytes)
203
 
        reader = pack.ContainerReader(stream)
204
 
        return reader
205
 
 
206
 
    def test_construct(self):
207
 
        """Test constructing a ContainerReader.
208
 
        
209
 
        This uses None as the output stream to show that the constructor doesn't
210
 
        try to use the input stream.
211
 
        """
212
 
        reader = pack.ContainerReader(None)
213
 
 
214
 
    def test_empty_container(self):
215
 
        """Read an empty container."""
216
 
        reader = self.get_reader_for(
217
 
            "Bazaar pack format 1 (introduced in 0.18)\nE")
218
 
        self.assertEqual([], list(reader.iter_records()))
219
 
 
220
 
    def test_unknown_format(self):
221
 
        """Unrecognised container formats raise UnknownContainerFormatError."""
222
 
        reader = self.get_reader_for("unknown format\n")
223
 
        self.assertRaises(
224
 
            errors.UnknownContainerFormatError, reader.iter_records)
225
 
 
226
 
    def test_unexpected_end_of_container(self):
227
 
        """Containers that don't end with an End Marker record should cause
228
 
        UnexpectedEndOfContainerError to be raised.
229
 
        """
230
 
        reader = self.get_reader_for(
231
 
            "Bazaar pack format 1 (introduced in 0.18)\n")
232
 
        iterator = reader.iter_records()
233
 
        self.assertRaises(
234
 
            errors.UnexpectedEndOfContainerError, iterator.next)
235
 
 
236
 
    def test_unknown_record_type(self):
237
 
        """Unknown record types cause UnknownRecordTypeError to be raised."""
238
 
        reader = self.get_reader_for(
239
 
            "Bazaar pack format 1 (introduced in 0.18)\nX")
240
 
        iterator = reader.iter_records()
241
 
        self.assertRaises(
242
 
            errors.UnknownRecordTypeError, iterator.next)
243
 
 
244
 
    def test_container_with_one_unnamed_record(self):
245
 
        """Read a container with one Bytes record.
246
 
        
247
 
        Parsing Bytes records is more thoroughly exercised by
248
 
        TestBytesRecordReader.  This test is here to ensure that
249
 
        ContainerReader's integration with BytesRecordReader is working.
250
 
        """
251
 
        reader = self.get_reader_for(
252
 
            "Bazaar pack format 1 (introduced in 0.18)\nB5\n\naaaaaE")
253
 
        expected_records = [([], 'aaaaa')]
254
 
        self.assertEqual(
255
 
            expected_records,
256
 
            [(names, read_bytes(None))
257
 
             for (names, read_bytes) in reader.iter_records()])
258
 
 
259
 
    def test_validate_empty_container(self):
260
 
        """validate does not raise an error for a container with no records."""
261
 
        reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
262
 
        # No exception raised
263
 
        reader.validate()
264
 
 
265
 
    def test_validate_non_empty_valid_container(self):
266
 
        """validate does not raise an error for a container with a valid record.
267
 
        """
268
 
        reader = self.get_reader_for(
269
 
            "Bazaar pack format 1 (introduced in 0.18)\nB3\nname\n\nabcE")
270
 
        # No exception raised
271
 
        reader.validate()
272
 
 
273
 
    def test_validate_bad_format(self):
274
 
        """validate raises an error for unrecognised format strings.
275
 
 
276
 
        It may raise either UnexpectedEndOfContainerError or
277
 
        UnknownContainerFormatError, depending on exactly what the string is.
278
 
        """
279
 
        inputs = ["", "x", "Bazaar pack format 1 (introduced in 0.18)", "bad\n"]
280
 
        for input in inputs:
281
 
            reader = self.get_reader_for(input)
282
 
            self.assertRaises(
283
 
                (errors.UnexpectedEndOfContainerError,
284
 
                 errors.UnknownContainerFormatError),
285
 
                reader.validate)
286
 
 
287
 
    def test_validate_bad_record_marker(self):
288
 
        """validate raises UnknownRecordTypeError for unrecognised record
289
 
        types.
290
 
        """
291
 
        reader = self.get_reader_for(
292
 
            "Bazaar pack format 1 (introduced in 0.18)\nX")
293
 
        self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
294
 
 
295
 
    def test_validate_data_after_end_marker(self):
296
 
        """validate raises ContainerHasExcessDataError if there are any bytes
297
 
        after the end of the container.
298
 
        """
299
 
        reader = self.get_reader_for(
300
 
            "Bazaar pack format 1 (introduced in 0.18)\nEcrud")
301
 
        self.assertRaises(
302
 
            errors.ContainerHasExcessDataError, reader.validate)
303
 
 
304
 
    def test_validate_no_end_marker(self):
305
 
        """validate raises UnexpectedEndOfContainerError if there's no end of
306
 
        container marker, even if the container up to this point has been valid.
307
 
        """
308
 
        reader = self.get_reader_for(
309
 
            "Bazaar pack format 1 (introduced in 0.18)\n")
310
 
        self.assertRaises(
311
 
            errors.UnexpectedEndOfContainerError, reader.validate)
312
 
 
313
 
    def test_validate_duplicate_name(self):
314
 
        """validate raises DuplicateRecordNameError if the same name occurs
315
 
        multiple times in the container.
316
 
        """
317
 
        reader = self.get_reader_for(
318
 
            "Bazaar pack format 1 (introduced in 0.18)\n"
319
 
            "B0\nname\n\n"
320
 
            "B0\nname\n\n"
321
 
            "E")
322
 
        self.assertRaises(errors.DuplicateRecordNameError, reader.validate)
323
 
 
324
 
    def test_validate_undecodeable_name(self):
325
 
        """Names that aren't valid UTF-8 cause validate to fail."""
326
 
        reader = self.get_reader_for(
327
 
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
328
 
        self.assertRaises(errors.InvalidRecordError, reader.validate)
329
 
        
330
 
 
331
 
class TestBytesRecordReader(tests.TestCase):
332
 
    """Tests for reading and validating Bytes records with
333
 
    BytesRecordReader.
334
 
    
335
 
    Like TestContainerReader, this explicitly tests the reading of format 1
336
 
    data.  If a new version of the format is added, then a separate set of
337
 
    tests for reading that format should be added.
338
 
    """
339
 
 
340
 
    def get_reader_for(self, bytes):
341
 
        stream = StringIO(bytes)
342
 
        reader = pack.BytesRecordReader(stream)
343
 
        return reader
344
 
 
345
 
    def test_record_with_no_name(self):
346
 
        """Reading a Bytes record with no name returns an empty list of
347
 
        names.
348
 
        """
349
 
        reader = self.get_reader_for("5\n\naaaaa")
350
 
        names, get_bytes = reader.read()
351
 
        self.assertEqual([], names)
352
 
        self.assertEqual('aaaaa', get_bytes(None))
353
 
 
354
 
    def test_record_with_one_name(self):
355
 
        """Reading a Bytes record with one name returns a list of just that
356
 
        name.
357
 
        """
358
 
        reader = self.get_reader_for("5\nname1\n\naaaaa")
359
 
        names, get_bytes = reader.read()
360
 
        self.assertEqual([('name1', )], names)
361
 
        self.assertEqual('aaaaa', get_bytes(None))
362
 
 
363
 
    def test_record_with_two_names(self):
364
 
        """Reading a Bytes record with two names returns a list of both names.
365
 
        """
366
 
        reader = self.get_reader_for("5\nname1\nname2\n\naaaaa")
367
 
        names, get_bytes = reader.read()
368
 
        self.assertEqual([('name1', ), ('name2', )], names)
369
 
        self.assertEqual('aaaaa', get_bytes(None))
370
 
 
371
 
    def test_record_with_two_part_names(self):
372
 
        """Reading a Bytes record with a two_part name reads both."""
373
 
        reader = self.get_reader_for("5\nname1\x00name2\n\naaaaa")
374
 
        names, get_bytes = reader.read()
375
 
        self.assertEqual([('name1', 'name2', )], names)
376
 
        self.assertEqual('aaaaa', get_bytes(None))
377
 
 
378
 
    def test_invalid_length(self):
379
 
        """If the length-prefix is not a number, parsing raises
380
 
        InvalidRecordError.
381
 
        """
382
 
        reader = self.get_reader_for("not a number\n")
383
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
384
 
 
385
 
    def test_early_eof(self):
386
 
        """Tests for premature EOF occuring during parsing Bytes records with
387
 
        BytesRecordReader.
388
 
        
389
 
        A incomplete container might be interrupted at any point.  The
390
 
        BytesRecordReader needs to cope with the input stream running out no
391
 
        matter where it is in the parsing process.
392
 
 
393
 
        In all cases, UnexpectedEndOfContainerError should be raised.
394
 
        """
395
 
        complete_record = "6\nname\n\nabcdef"
396
 
        for count in range(0, len(complete_record)):
397
 
            incomplete_record = complete_record[:count]
398
 
            reader = self.get_reader_for(incomplete_record)
399
 
            # We don't use assertRaises to make diagnosing failures easier
400
 
            # (assertRaises doesn't allow a custom failure message).
401
 
            try:
402
 
                names, read_bytes = reader.read()
403
 
                read_bytes(None)
404
 
            except errors.UnexpectedEndOfContainerError:
405
 
                pass
406
 
            else:
407
 
                self.fail(
408
 
                    "UnexpectedEndOfContainerError not raised when parsing %r"
409
 
                    % (incomplete_record,))
410
 
 
411
 
    def test_initial_eof(self):
412
 
        """EOF before any bytes read at all."""
413
 
        reader = self.get_reader_for("")
414
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
415
 
 
416
 
    def test_eof_after_length(self):
417
 
        """EOF after reading the length and before reading name(s)."""
418
 
        reader = self.get_reader_for("123\n")
419
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
420
 
 
421
 
    def test_eof_during_name(self):
422
 
        """EOF during reading a name."""
423
 
        reader = self.get_reader_for("123\nname")
424
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
425
 
 
426
 
    def test_read_invalid_name_whitespace(self):
427
 
        """Names must have no whitespace."""
428
 
        # A name with a space.
429
 
        reader = self.get_reader_for("0\nbad name\n\n")
430
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
431
 
 
432
 
        # A name with a tab.
433
 
        reader = self.get_reader_for("0\nbad\tname\n\n")
434
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
435
 
 
436
 
        # A name with a vertical tab.
437
 
        reader = self.get_reader_for("0\nbad\vname\n\n")
438
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
439
 
 
440
 
    def test_validate_whitespace_in_name(self):
441
 
        """Names must have no whitespace."""
442
 
        reader = self.get_reader_for("0\nbad name\n\n")
443
 
        self.assertRaises(errors.InvalidRecordError, reader.validate)
444
 
 
445
 
    def test_validate_interrupted_prelude(self):
446
 
        """EOF during reading a record's prelude causes validate to fail."""
447
 
        reader = self.get_reader_for("")
448
 
        self.assertRaises(
449
 
            errors.UnexpectedEndOfContainerError, reader.validate)
450
 
 
451
 
    def test_validate_interrupted_body(self):
452
 
        """EOF during reading a record's body causes validate to fail."""
453
 
        reader = self.get_reader_for("1\n\n")
454
 
        self.assertRaises(
455
 
            errors.UnexpectedEndOfContainerError, reader.validate)
456
 
 
457
 
    def test_validate_unparseable_length(self):
458
 
        """An unparseable record length causes validate to fail."""
459
 
        reader = self.get_reader_for("\n\n")
460
 
        self.assertRaises(
461
 
            errors.InvalidRecordError, reader.validate)
462
 
 
463
 
    def test_validate_undecodeable_name(self):
464
 
        """Names that aren't valid UTF-8 cause validate to fail."""
465
 
        reader = self.get_reader_for("0\n\xcc\n\n")
466
 
        self.assertRaises(errors.InvalidRecordError, reader.validate)
467
 
 
468
 
    def test_read_max_length(self):
469
 
        """If the max_length passed to the callable returned by read is not
470
 
        None, then no more than that many bytes will be read.
471
 
        """
472
 
        reader = self.get_reader_for("6\n\nabcdef")
473
 
        names, get_bytes = reader.read()
474
 
        self.assertEqual('abc', get_bytes(3))
475
 
 
476
 
    def test_read_no_max_length(self):
477
 
        """If the max_length passed to the callable returned by read is None,
478
 
        then all the bytes in the record will be read.
479
 
        """
480
 
        reader = self.get_reader_for("6\n\nabcdef")
481
 
        names, get_bytes = reader.read()
482
 
        self.assertEqual('abcdef', get_bytes(None))
483
 
 
484
 
    def test_repeated_read_calls(self):
485
 
        """Repeated calls to the callable returned from BytesRecordReader.read
486
 
        will not read beyond the end of the record.
487
 
        """
488
 
        reader = self.get_reader_for("6\n\nabcdefB3\nnext-record\nXXX")
489
 
        names, get_bytes = reader.read()
490
 
        self.assertEqual('abcdef', get_bytes(None))
491
 
        self.assertEqual('', get_bytes(None))
492
 
        self.assertEqual('', get_bytes(99))
493
 
 
494
 
 
495
 
class TestMakeReadvReader(tests.TestCaseWithTransport):
496
 
 
497
 
    def test_read_skipping_records(self):
498
 
        pack_data = StringIO()
499
 
        writer = pack.ContainerWriter(pack_data.write)
500
 
        writer.begin()
501
 
        memos = []
502
 
        memos.append(writer.add_bytes_record('abc', names=[]))
503
 
        memos.append(writer.add_bytes_record('def', names=[('name1', )]))
504
 
        memos.append(writer.add_bytes_record('ghi', names=[('name2', )]))
505
 
        memos.append(writer.add_bytes_record('jkl', names=[]))
506
 
        writer.end()
507
 
        transport = self.get_transport()
508
 
        transport.put_bytes('mypack', pack_data.getvalue())
509
 
        requested_records = [memos[0], memos[2]]
510
 
        reader = pack.make_readv_reader(transport, 'mypack', requested_records)
511
 
        result = []
512
 
        for names, reader_func in reader.iter_records():
513
 
            result.append((names, reader_func(None)))
514
 
        self.assertEqual([([], 'abc'), ([('name2', )], 'ghi')], result)
515
 
 
516
 
 
517
 
class TestReadvFile(tests.TestCaseWithTransport):
518
 
    """Tests of the ReadVFile class.
519
 
 
520
 
    Error cases are deliberately undefined: this code adapts the underlying
521
 
    transport interface to a single 'streaming read' interface as 
522
 
    ContainerReader needs.
523
 
    """
524
 
 
525
 
    def test_read_bytes(self):
526
 
        """Test reading of both single bytes and all bytes in a hunk."""
527
 
        transport = self.get_transport()
528
 
        transport.put_bytes('sample', '0123456789')
529
 
        f = pack.ReadVFile(transport.readv('sample', [(0,1), (1,2), (4,1), (6,2)]))
530
 
        results = []
531
 
        results.append(f.read(1))
532
 
        results.append(f.read(2))
533
 
        results.append(f.read(1))
534
 
        results.append(f.read(1))
535
 
        results.append(f.read(1))
536
 
        self.assertEqual(['0', '12', '4', '6', '7'], results)
537
 
 
538
 
    def test_readline(self):
539
 
        """Test using readline() as ContainerReader does.
540
 
 
541
 
        This is always within a readv hunk, never across it.
542
 
        """
543
 
        transport = self.get_transport()
544
 
        transport.put_bytes('sample', '0\n2\n4\n')
545
 
        f = pack.ReadVFile(transport.readv('sample', [(0,2), (2,4)]))
546
 
        results = []
547
 
        results.append(f.readline())
548
 
        results.append(f.readline())
549
 
        results.append(f.readline())
550
 
        self.assertEqual(['0\n', '2\n', '4\n'], results)
551
 
 
552
 
    def test_readline_and_read(self):
553
 
        """Test exercising one byte reads, readline, and then read again."""
554
 
        transport = self.get_transport()
555
 
        transport.put_bytes('sample', '0\n2\n4\n')
556
 
        f = pack.ReadVFile(transport.readv('sample', [(0,6)]))
557
 
        results = []
558
 
        results.append(f.read(1))
559
 
        results.append(f.readline())
560
 
        results.append(f.read(4))
561
 
        self.assertEqual(['0', '\n', '2\n4\n'], results)
562
 
 
563
 
 
564
 
class PushParserTestCase(tests.TestCase):
565
 
    """Base class for TestCases involving ContainerPushParser."""
566
 
 
567
 
    def make_parser_expecting_record_type(self):
568
 
        parser = pack.ContainerPushParser()
569
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\n")
570
 
        return parser
571
 
 
572
 
    def make_parser_expecting_bytes_record(self):
573
 
        parser = pack.ContainerPushParser()
574
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\nB")
575
 
        return parser
576
 
 
577
 
    def assertRecordParsing(self, expected_record, bytes):
578
 
        """Assert that 'bytes' is parsed as a given bytes record.
579
 
 
580
 
        :param expected_record: A tuple of (names, bytes).
581
 
        """
582
 
        parser = self.make_parser_expecting_bytes_record()
583
 
        parser.accept_bytes(bytes)
584
 
        parsed_records = parser.read_pending_records()
585
 
        self.assertEqual([expected_record], parsed_records)
586
 
 
587
 
        
588
 
class TestContainerPushParser(PushParserTestCase):
589
 
    """Tests for ContainerPushParser.
590
 
    
591
 
    The ContainerPushParser reads format 1 containers, so these tests
592
 
    explicitly test how it reacts to format 1 data.  If a new version of the
593
 
    format is added, then separate tests for that format should be added.
594
 
    """
595
 
 
596
 
    def test_construct(self):
597
 
        """ContainerPushParser can be constructed."""
598
 
        pack.ContainerPushParser()
599
 
 
600
 
    def test_multiple_records_at_once(self):
601
 
        """If multiple records worth of data are fed to the parser in one
602
 
        string, the parser will correctly parse all the records.
603
 
 
604
 
        (A naive implementation might stop after parsing the first record.)
605
 
        """
606
 
        parser = self.make_parser_expecting_record_type()
607
 
        parser.accept_bytes("B5\nname1\n\nbody1B5\nname2\n\nbody2")
608
 
        self.assertEqual(
609
 
            [([('name1',)], 'body1'), ([('name2',)], 'body2')],
610
 
            parser.read_pending_records())
611
 
 
612
 
 
613
 
class TestContainerPushParserBytesParsing(PushParserTestCase):
614
 
    """Tests for reading Bytes records with ContainerPushParser.
615
 
    
616
 
    The ContainerPushParser reads format 1 containers, so these tests
617
 
    explicitly test how it reacts to format 1 data.  If a new version of the
618
 
    format is added, then separate tests for that format should be added.
619
 
    """
620
 
 
621
 
    def test_record_with_no_name(self):
622
 
        """Reading a Bytes record with no name returns an empty list of
623
 
        names.
624
 
        """
625
 
        self.assertRecordParsing(([], 'aaaaa'), "5\n\naaaaa")
626
 
 
627
 
    def test_record_with_one_name(self):
628
 
        """Reading a Bytes record with one name returns a list of just that
629
 
        name.
630
 
        """
631
 
        self.assertRecordParsing(
632
 
            ([('name1', )], 'aaaaa'),
633
 
            "5\nname1\n\naaaaa")
634
 
 
635
 
    def test_record_with_two_names(self):
636
 
        """Reading a Bytes record with two names returns a list of both names.
637
 
        """
638
 
        self.assertRecordParsing(
639
 
            ([('name1', ), ('name2', )], 'aaaaa'),
640
 
            "5\nname1\nname2\n\naaaaa")
641
 
 
642
 
    def test_record_with_two_part_names(self):
643
 
        """Reading a Bytes record with a two_part name reads both."""
644
 
        self.assertRecordParsing(
645
 
            ([('name1', 'name2')], 'aaaaa'),
646
 
            "5\nname1\x00name2\n\naaaaa")
647
 
 
648
 
    def test_invalid_length(self):
649
 
        """If the length-prefix is not a number, parsing raises
650
 
        InvalidRecordError.
651
 
        """
652
 
        parser = self.make_parser_expecting_bytes_record()
653
 
        self.assertRaises(
654
 
            errors.InvalidRecordError, parser.accept_bytes, "not a number\n")
655
 
 
656
 
    def test_incomplete_record(self):
657
 
        """If the bytes seen so far don't form a complete record, then there
658
 
        will be nothing returned by read_pending_records.
659
 
        """
660
 
        parser = self.make_parser_expecting_bytes_record()
661
 
        parser.accept_bytes("5\n\nabcd")
662
 
        self.assertEqual([], parser.read_pending_records())
663
 
 
664
 
    def test_accept_nothing(self):
665
 
        """The edge case of parsing an empty string causes no error."""
666
 
        parser = self.make_parser_expecting_bytes_record()
667
 
        parser.accept_bytes("")
668
 
 
669
 
    def assertInvalidRecord(self, bytes):
670
 
        """Assert that parsing the given bytes will raise an
671
 
        InvalidRecordError.
672
 
        """
673
 
        parser = self.make_parser_expecting_bytes_record()
674
 
        self.assertRaises(
675
 
            errors.InvalidRecordError, parser.accept_bytes, bytes)
676
 
 
677
 
    def test_read_invalid_name_whitespace(self):
678
 
        """Names must have no whitespace."""
679
 
        # A name with a space.
680
 
        self.assertInvalidRecord("0\nbad name\n\n")
681
 
 
682
 
        # A name with a tab.
683
 
        self.assertInvalidRecord("0\nbad\tname\n\n")
684
 
 
685
 
        # A name with a vertical tab.
686
 
        self.assertInvalidRecord("0\nbad\vname\n\n")
687
 
 
688
 
    def test_repeated_read_pending_records(self):
689
 
        """read_pending_records will not return the same record twice."""
690
 
        parser = self.make_parser_expecting_bytes_record()
691
 
        parser.accept_bytes("6\n\nabcdef")
692
 
        self.assertEqual([([], 'abcdef')], parser.read_pending_records())
693
 
        self.assertEqual([], parser.read_pending_records())
694
 
 
695