~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Martin Pool
  • Date: 2009-06-19 10:00:56 UTC
  • mto: This revision was merged to the branch mainline in revision 4464.
  • Revision ID: mbp@sourcefrog.net-20090619100056-fco5ooae2ybl88ne
Fix copyrights and remove assert statement from doc_generate

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