~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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2506.3.1 by Andrew Bennetts
More progress:
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
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
22
from bzrlib import pack, errors, tests
23
24
25
class TestContainerWriter(tests.TestCase):
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
26
27
    def test_construct(self):
28
        """Test constructing a ContainerWriter.
29
        
30
        This uses None as the output stream to show that the constructor doesn't
31
        try to use the output stream.
32
        """
2506.3.1 by Andrew Bennetts
More progress:
33
        writer = pack.ContainerWriter(None)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
34
35
    def test_begin(self):
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
36
        """The begin() method writes the container format marker line."""
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
37
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
38
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
39
        writer.begin()
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
40
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
41
                         output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
42
43
    def test_end(self):
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
44
        """The end() method writes an End Marker record."""
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
45
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
46
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
47
        writer.begin()
48
        writer.end()
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
49
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
50
                         output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
51
52
    def test_add_bytes_record_no_name(self):
53
        """Add a bytes record with no name."""
54
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
55
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
56
        writer.begin()
2661.2.1 by Robert Collins
* ``bzrlib.pack.ContainerWriter`` now returns an offset, length tuple to
57
        offset, length = writer.add_bytes_record('abc', names=[])
58
        self.assertEqual((42, 7), (offset, length))
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
59
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
60
                         output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
61
62
    def test_add_bytes_record_one_name(self):
63
        """Add a bytes record with one name."""
64
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
65
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
66
        writer.begin()
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
67
        offset, length = writer.add_bytes_record('abc', names=[('name1', )])
2661.2.1 by Robert Collins
* ``bzrlib.pack.ContainerWriter`` now returns an offset, length tuple to
68
        self.assertEqual((42, 13), (offset, length))
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
69
        self.assertEqual(
70
            'Bazaar pack format 1 (introduced in 0.18)\n'
71
            'B3\nname1\n\nabc',
72
            output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
73
74
    def test_add_bytes_record_two_names(self):
75
        """Add a bytes record with two names."""
76
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
77
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
78
        writer.begin()
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
79
        offset, length = writer.add_bytes_record('abc', names=[('name1', ), ('name2', )])
80
        self.assertEqual((42, 19), (offset, length))
81
        self.assertEqual(
82
            'Bazaar pack format 1 (introduced in 0.18)\n'
83
            'B3\nname1\nname2\n\nabc',
84
            output.getvalue())
85
86
    def test_add_bytes_record_two_names(self):
87
        """Add a bytes record with two names."""
88
        output = StringIO()
89
        writer = pack.ContainerWriter(output.write)
90
        writer.begin()
91
        offset, length = writer.add_bytes_record('abc', names=[('name1', ), ('name2', )])
92
        self.assertEqual((42, 19), (offset, length))
93
        self.assertEqual(
94
            'Bazaar pack format 1 (introduced in 0.18)\n'
95
            'B3\nname1\nname2\n\nabc',
96
            output.getvalue())
97
98
    def test_add_bytes_record_two_element_name(self):
99
        """Add a bytes record with a two-element name."""
100
        output = StringIO()
101
        writer = pack.ContainerWriter(output.write)
102
        writer.begin()
103
        offset, length = writer.add_bytes_record('abc', names=[('name1', 'name2')])
104
        self.assertEqual((42, 19), (offset, length))
105
        self.assertEqual(
106
            'Bazaar pack format 1 (introduced in 0.18)\n'
107
            'B3\nname1\x00name2\n\nabc',
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
108
            output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
109
2661.2.1 by Robert Collins
* ``bzrlib.pack.ContainerWriter`` now returns an offset, length tuple to
110
    def test_add_second_bytes_record_gets_higher_offset(self):
111
        output = StringIO()
112
        writer = pack.ContainerWriter(output.write)
113
        writer.begin()
114
        writer.add_bytes_record('abc', names=[])
115
        offset, length = writer.add_bytes_record('abc', names=[])
116
        self.assertEqual((49, 7), (offset, length))
117
        self.assertEqual(
118
            'Bazaar pack format 1 (introduced in 0.18)\n'
119
            'B3\n\nabc'
120
            'B3\n\nabc',
121
            output.getvalue())
122
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
123
    def test_add_bytes_record_invalid_name(self):
124
        """Adding a Bytes record with a name with whitespace in it raises
125
        InvalidRecordError.
126
        """
127
        output = StringIO()
128
        writer = pack.ContainerWriter(output.write)
129
        writer.begin()
130
        self.assertRaises(
131
            errors.InvalidRecordError,
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
132
            writer.add_bytes_record, 'abc', names=[('bad name', )])
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
133
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
134
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
135
class TestContainerReader(tests.TestCase):
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
136
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
137
    def get_reader_for(self, bytes):
138
        stream = StringIO(bytes)
2506.2.9 by Aaron Bentley
Use file-like objects as container input, not callables
139
        reader = pack.ContainerReader(stream)
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
140
        return reader
141
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
142
    def test_construct(self):
143
        """Test constructing a ContainerReader.
144
        
145
        This uses None as the output stream to show that the constructor doesn't
146
        try to use the input stream.
147
        """
2506.3.1 by Andrew Bennetts
More progress:
148
        reader = pack.ContainerReader(None)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
149
150
    def test_empty_container(self):
151
        """Read an empty container."""
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
152
        reader = self.get_reader_for(
153
            "Bazaar pack format 1 (introduced in 0.18)\nE")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
154
        self.assertEqual([], list(reader.iter_records()))
155
156
    def test_unknown_format(self):
157
        """Unrecognised container formats raise UnknownContainerFormatError."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
158
        reader = self.get_reader_for("unknown format\n")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
159
        self.assertRaises(
160
            errors.UnknownContainerFormatError, reader.iter_records)
161
162
    def test_unexpected_end_of_container(self):
163
        """Containers that don't end with an End Marker record should cause
164
        UnexpectedEndOfContainerError to be raised.
165
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
166
        reader = self.get_reader_for(
167
            "Bazaar pack format 1 (introduced in 0.18)\n")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
168
        iterator = reader.iter_records()
169
        self.assertRaises(
170
            errors.UnexpectedEndOfContainerError, iterator.next)
171
172
    def test_unknown_record_type(self):
173
        """Unknown record types cause UnknownRecordTypeError to be raised."""
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
174
        reader = self.get_reader_for(
175
            "Bazaar pack format 1 (introduced in 0.18)\nX")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
176
        iterator = reader.iter_records()
177
        self.assertRaises(
178
            errors.UnknownRecordTypeError, iterator.next)
179
2506.3.1 by Andrew Bennetts
More progress:
180
    def test_container_with_one_unnamed_record(self):
181
        """Read a container with one Bytes record.
182
        
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
183
        Parsing Bytes records is more thoroughly exercised by
184
        TestBytesRecordReader.  This test is here to ensure that
185
        ContainerReader's integration with BytesRecordReader is working.
2506.3.1 by Andrew Bennetts
More progress:
186
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
187
        reader = self.get_reader_for(
188
            "Bazaar pack format 1 (introduced in 0.18)\nB5\n\naaaaaE")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
189
        expected_records = [([], 'aaaaa')]
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
190
        self.assertEqual(
191
            expected_records,
192
            [(names, read_bytes(None))
193
             for (names, read_bytes) in reader.iter_records()])
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
194
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
195
    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.
196
        """validate does not raise an error for a container with no records."""
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
197
        reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
198
        # No exception raised
199
        reader.validate()
200
201
    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.
202
        """validate does not raise an error for a container with a valid record.
203
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
204
        reader = self.get_reader_for(
205
            "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.
206
        # No exception raised
207
        reader.validate()
208
209
    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.
210
        """validate raises an error for unrecognised format strings.
211
212
        It may raise either UnexpectedEndOfContainerError or
213
        UnknownContainerFormatError, depending on exactly what the string is.
214
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
215
        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.
216
        for input in inputs:
217
            reader = self.get_reader_for(input)
218
            self.assertRaises(
219
                (errors.UnexpectedEndOfContainerError,
220
                 errors.UnknownContainerFormatError),
221
                reader.validate)
222
223
    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.
224
        """validate raises UnknownRecordTypeError for unrecognised record
225
        types.
226
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
227
        reader = self.get_reader_for(
228
            "Bazaar pack format 1 (introduced in 0.18)\nX")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
229
        self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
230
231
    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.
232
        """validate raises ContainerHasExcessDataError if there are any bytes
233
        after the end of the container.
234
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
235
        reader = self.get_reader_for(
236
            "Bazaar pack format 1 (introduced in 0.18)\nEcrud")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
237
        self.assertRaises(
238
            errors.ContainerHasExcessDataError, reader.validate)
239
240
    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.
241
        """validate raises UnexpectedEndOfContainerError if there's no end of
242
        container marker, even if the container up to this point has been valid.
243
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
244
        reader = self.get_reader_for(
245
            "Bazaar pack format 1 (introduced in 0.18)\n")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
246
        self.assertRaises(
247
            errors.UnexpectedEndOfContainerError, reader.validate)
248
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
249
    def test_validate_duplicate_name(self):
250
        """validate raises DuplicateRecordNameError if the same name occurs
251
        multiple times in the container.
252
        """
253
        reader = self.get_reader_for(
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
254
            "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.
255
            "B0\nname\n\n"
256
            "B0\nname\n\n"
257
            "E")
258
        self.assertRaises(errors.DuplicateRecordNameError, reader.validate)
259
260
    def test_validate_undecodeable_name(self):
261
        """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.
262
        reader = self.get_reader_for(
263
            "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.
264
        self.assertRaises(errors.InvalidRecordError, reader.validate)
265
        
2506.3.1 by Andrew Bennetts
More progress:
266
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
267
class TestBytesRecordReader(tests.TestCase):
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
268
    """Tests for reading and validating Bytes records with BytesRecordReader."""
2506.3.1 by Andrew Bennetts
More progress:
269
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
270
    def get_reader_for(self, bytes):
271
        stream = StringIO(bytes)
2506.2.9 by Aaron Bentley
Use file-like objects as container input, not callables
272
        reader = pack.BytesRecordReader(stream)
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
273
        return reader
274
2506.3.1 by Andrew Bennetts
More progress:
275
    def test_record_with_no_name(self):
276
        """Reading a Bytes record with no name returns an empty list of
277
        names.
278
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
279
        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.
280
        names, get_bytes = reader.read()
2506.3.1 by Andrew Bennetts
More progress:
281
        self.assertEqual([], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
282
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
283
284
    def test_record_with_one_name(self):
285
        """Reading a Bytes record with one name returns a list of just that
286
        name.
287
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
288
        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.
289
        names, get_bytes = reader.read()
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
290
        self.assertEqual([('name1', )], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
291
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
292
293
    def test_record_with_two_names(self):
294
        """Reading a Bytes record with two names returns a list of both names.
295
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
296
        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.
297
        names, get_bytes = reader.read()
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
298
        self.assertEqual([('name1', ), ('name2', )], names)
299
        self.assertEqual('aaaaa', get_bytes(None))
300
301
    def test_record_with_two_part_names(self):
302
        """Reading a Bytes record with a two_part name reads both."""
303
        reader = self.get_reader_for("5\nname1\x00name2\n\naaaaa")
304
        names, get_bytes = reader.read()
305
        self.assertEqual([('name1', 'name2', )], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
306
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
307
308
    def test_invalid_length(self):
309
        """If the length-prefix is not a number, parsing raises
310
        InvalidRecordError.
311
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
312
        reader = self.get_reader_for("not a number\n")
2506.3.1 by Andrew Bennetts
More progress:
313
        self.assertRaises(errors.InvalidRecordError, reader.read)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
314
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
315
    def test_early_eof(self):
316
        """Tests for premature EOF occuring during parsing Bytes records with
317
        BytesRecordReader.
318
        
319
        A incomplete container might be interrupted at any point.  The
320
        BytesRecordReader needs to cope with the input stream running out no
321
        matter where it is in the parsing process.
322
323
        In all cases, UnexpectedEndOfContainerError should be raised.
324
        """
325
        complete_record = "6\nname\n\nabcdef"
326
        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.
327
            incomplete_record = complete_record[:count]
328
            reader = self.get_reader_for(incomplete_record)
329
            # We don't use assertRaises to make diagnosing failures easier
330
            # (assertRaises doesn't allow a custom failure message).
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
331
            try:
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
332
                names, read_bytes = reader.read()
333
                read_bytes(None)
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
334
            except errors.UnexpectedEndOfContainerError:
335
                pass
336
            else:
337
                self.fail(
338
                    "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.
339
                    % (incomplete_record,))
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
340
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
341
    def test_initial_eof(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
342
        """EOF before any bytes read at all."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
343
        reader = self.get_reader_for("")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
344
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
345
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
346
    def test_eof_after_length(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
347
        """EOF after reading the length and before reading name(s)."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
348
        reader = self.get_reader_for("123\n")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
349
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
350
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
351
    def test_eof_during_name(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
352
        """EOF during reading a name."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
353
        reader = self.get_reader_for("123\nname")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
354
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
355
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
356
    def test_read_invalid_name_whitespace(self):
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
357
        """Names must have no whitespace."""
358
        # A name with a space.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
359
        reader = self.get_reader_for("0\nbad name\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
360
        self.assertRaises(errors.InvalidRecordError, reader.read)
361
362
        # A name with a tab.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
363
        reader = self.get_reader_for("0\nbad\tname\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
364
        self.assertRaises(errors.InvalidRecordError, reader.read)
365
366
        # A name with a vertical tab.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
367
        reader = self.get_reader_for("0\nbad\vname\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
368
        self.assertRaises(errors.InvalidRecordError, reader.read)
369
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
370
    def test_validate_whitespace_in_name(self):
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
371
        """Names must have no whitespace."""
372
        reader = self.get_reader_for("0\nbad name\n\n")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
373
        self.assertRaises(errors.InvalidRecordError, reader.validate)
374
375
    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.
376
        """EOF during reading a record's prelude causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
377
        reader = self.get_reader_for("")
378
        self.assertRaises(
379
            errors.UnexpectedEndOfContainerError, reader.validate)
380
381
    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.
382
        """EOF during reading a record's body causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
383
        reader = self.get_reader_for("1\n\n")
384
        self.assertRaises(
385
            errors.UnexpectedEndOfContainerError, reader.validate)
386
387
    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.
388
        """An unparseable record length causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
389
        reader = self.get_reader_for("\n\n")
390
        self.assertRaises(
391
            errors.InvalidRecordError, reader.validate)
392
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
393
    def test_validate_undecodeable_name(self):
394
        """Names that aren't valid UTF-8 cause validate to fail."""
395
        reader = self.get_reader_for("0\n\xcc\n\n")
396
        self.assertRaises(errors.InvalidRecordError, reader.validate)
397
398
    def test_read_max_length(self):
399
        """If the max_length passed to the callable returned by read is not
400
        None, then no more than that many bytes will be read.
401
        """
402
        reader = self.get_reader_for("6\n\nabcdef")
403
        names, get_bytes = reader.read()
404
        self.assertEqual('abc', get_bytes(3))
405
406
    def test_read_no_max_length(self):
407
        """If the max_length passed to the callable returned by read is None,
408
        then all the bytes in the record will be read.
409
        """
410
        reader = self.get_reader_for("6\n\nabcdef")
411
        names, get_bytes = reader.read()
412
        self.assertEqual('abcdef', get_bytes(None))
413
414
    def test_repeated_read_calls(self):
415
        """Repeated calls to the callable returned from BytesRecordReader.read
416
        will not read beyond the end of the record.
417
        """
418
        reader = self.get_reader_for("6\n\nabcdefB3\nnext-record\nXXX")
419
        names, get_bytes = reader.read()
420
        self.assertEqual('abcdef', get_bytes(None))
421
        self.assertEqual('', get_bytes(None))
422
        self.assertEqual('', get_bytes(99))
423
424
2661.2.3 by Robert Collins
Review feedback.
425
class TestMakeReadvReader(tests.TestCaseWithTransport):
2661.2.2 by Robert Collins
* ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
426
427
    def test_read_skipping_records(self):
428
        pack_data = StringIO()
429
        writer = pack.ContainerWriter(pack_data.write)
430
        writer.begin()
431
        memos = []
432
        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
433
        memos.append(writer.add_bytes_record('def', names=[('name1', )]))
434
        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
435
        memos.append(writer.add_bytes_record('jkl', names=[]))
436
        writer.end()
437
        transport = self.get_transport()
2661.2.3 by Robert Collins
Review feedback.
438
        transport.put_bytes('mypack', pack_data.getvalue())
2661.2.2 by Robert Collins
* ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
439
        requested_records = [memos[0], memos[2]]
440
        reader = pack.make_readv_reader(transport, 'mypack', requested_records)
441
        result = []
442
        for names, reader_func in reader.iter_records():
443
            result.append((names, reader_func(None)))
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
444
        self.assertEqual([([], 'abc'), ([('name2', )], 'ghi')], result)
2661.2.2 by Robert Collins
* ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
445
446
447
class TestReadvFile(tests.TestCaseWithTransport):
2661.2.3 by Robert Collins
Review feedback.
448
    """Tests of the ReadVFile class.
449
450
    Error cases are deliberately undefined: this code adapts the underlying
451
    transport interface to a single 'streaming read' interface as 
452
    ContainerReader needs.
453
    """
2661.2.2 by Robert Collins
* ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
454
455
    def test_read_bytes(self):
2661.2.3 by Robert Collins
Review feedback.
456
        """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
457
        transport = self.get_transport()
458
        transport.put_bytes('sample', '0123456789')
459
        f = pack.ReadVFile(transport.readv('sample', [(0,1), (1,2), (4,1), (6,2)]))
460
        results = []
461
        results.append(f.read(1))
462
        results.append(f.read(2))
463
        results.append(f.read(1))
464
        results.append(f.read(1))
465
        results.append(f.read(1))
466
        self.assertEqual(['0', '12', '4', '6', '7'], results)
467
468
    def test_readline(self):
2661.2.3 by Robert Collins
Review feedback.
469
        """Test using readline() as ContainerReader does.
470
471
        This is always within a readv hunk, never across it.
472
        """
2661.2.2 by Robert Collins
* ``bzrlib.pack.make_readv_reader`` allows readv based access to pack
473
        transport = self.get_transport()
474
        transport.put_bytes('sample', '0\n2\n4\n')
475
        f = pack.ReadVFile(transport.readv('sample', [(0,2), (2,4)]))
476
        results = []
477
        results.append(f.readline())
478
        results.append(f.readline())
479
        results.append(f.readline())
480
        self.assertEqual(['0\n', '2\n', '4\n'], results)
481
482
    def test_readline_and_read(self):
2661.2.3 by Robert Collins
Review feedback.
483
        """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
484
        transport = self.get_transport()
485
        transport.put_bytes('sample', '0\n2\n4\n')
486
        f = pack.ReadVFile(transport.readv('sample', [(0,6)]))
487
        results = []
488
        results.append(f.read(1))
489
        results.append(f.readline())
490
        results.append(f.read(4))
491
        self.assertEqual(['0', '\n', '2\n4\n'], results)