/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_pack.py

  • Committer: Jelmer Vernooij
  • Date: 2017-08-07 11:49:46 UTC
  • mto: (6747.3.4 avoid-set-revid-3)
  • mto: This revision was merged to the branch mainline in revision 6750.
  • Revision ID: jelmer@jelmer.uk-20170807114946-luclmxuawyzhpiot
Avoid setting revision_ids.

Show diffs side-by-side

added added

removed removed

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