~bzr-pqm/bzr/bzr.dev

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