~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()
57
        writer.add_bytes_record('abc', names=[])
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
58
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
59
                         output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
60
61
    def test_add_bytes_record_one_name(self):
62
        """Add a bytes record with one name."""
63
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
64
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
65
        writer.begin()
66
        writer.add_bytes_record('abc', names=['name1'])
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
67
        self.assertEqual(
68
            'Bazaar pack format 1 (introduced in 0.18)\n'
69
            'B3\nname1\n\nabc',
70
            output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
71
72
    def test_add_bytes_record_two_names(self):
73
        """Add a bytes record with two names."""
74
        output = StringIO()
2506.3.1 by Andrew Bennetts
More progress:
75
        writer = pack.ContainerWriter(output.write)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
76
        writer.begin()
77
        writer.add_bytes_record('abc', names=['name1', 'name2'])
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
78
        self.assertEqual(
79
            'Bazaar pack format 1 (introduced in 0.18)\n'
80
            'B3\nname1\nname2\n\nabc',
81
            output.getvalue())
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
82
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
83
    def test_add_bytes_record_invalid_name(self):
84
        """Adding a Bytes record with a name with whitespace in it raises
85
        InvalidRecordError.
86
        """
87
        output = StringIO()
88
        writer = pack.ContainerWriter(output.write)
89
        writer.begin()
90
        self.assertRaises(
91
            errors.InvalidRecordError,
92
            writer.add_bytes_record, 'abc', names=['bad name'])
93
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
94
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
95
class TestContainerReader(tests.TestCase):
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
96
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
97
    def get_reader_for(self, bytes):
98
        stream = StringIO(bytes)
2506.2.9 by Aaron Bentley
Use file-like objects as container input, not callables
99
        reader = pack.ContainerReader(stream)
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
100
        return reader
101
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
102
    def test_construct(self):
103
        """Test constructing a ContainerReader.
104
        
105
        This uses None as the output stream to show that the constructor doesn't
106
        try to use the input stream.
107
        """
2506.3.1 by Andrew Bennetts
More progress:
108
        reader = pack.ContainerReader(None)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
109
110
    def test_empty_container(self):
111
        """Read an empty container."""
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
112
        reader = self.get_reader_for(
113
            "Bazaar pack format 1 (introduced in 0.18)\nE")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
114
        self.assertEqual([], list(reader.iter_records()))
115
116
    def test_unknown_format(self):
117
        """Unrecognised container formats raise UnknownContainerFormatError."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
118
        reader = self.get_reader_for("unknown format\n")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
119
        self.assertRaises(
120
            errors.UnknownContainerFormatError, reader.iter_records)
121
122
    def test_unexpected_end_of_container(self):
123
        """Containers that don't end with an End Marker record should cause
124
        UnexpectedEndOfContainerError to be raised.
125
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
126
        reader = self.get_reader_for(
127
            "Bazaar pack format 1 (introduced in 0.18)\n")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
128
        iterator = reader.iter_records()
129
        self.assertRaises(
130
            errors.UnexpectedEndOfContainerError, iterator.next)
131
132
    def test_unknown_record_type(self):
133
        """Unknown record types cause UnknownRecordTypeError to be raised."""
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
134
        reader = self.get_reader_for(
135
            "Bazaar pack format 1 (introduced in 0.18)\nX")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
136
        iterator = reader.iter_records()
137
        self.assertRaises(
138
            errors.UnknownRecordTypeError, iterator.next)
139
2506.3.1 by Andrew Bennetts
More progress:
140
    def test_container_with_one_unnamed_record(self):
141
        """Read a container with one Bytes record.
142
        
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
143
        Parsing Bytes records is more thoroughly exercised by
144
        TestBytesRecordReader.  This test is here to ensure that
145
        ContainerReader's integration with BytesRecordReader is working.
2506.3.1 by Andrew Bennetts
More progress:
146
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
147
        reader = self.get_reader_for(
148
            "Bazaar pack format 1 (introduced in 0.18)\nB5\n\naaaaaE")
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
149
        expected_records = [([], 'aaaaa')]
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
150
        self.assertEqual(
151
            expected_records,
152
            [(names, read_bytes(None))
153
             for (names, read_bytes) in reader.iter_records()])
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
154
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
155
    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.
156
        """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.
157
        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.
158
        # No exception raised
159
        reader.validate()
160
161
    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.
162
        """validate does not raise an error for a container with a valid record.
163
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
164
        reader = self.get_reader_for(
165
            "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.
166
        # No exception raised
167
        reader.validate()
168
169
    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.
170
        """validate raises an error for unrecognised format strings.
171
172
        It may raise either UnexpectedEndOfContainerError or
173
        UnknownContainerFormatError, depending on exactly what the string is.
174
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
175
        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.
176
        for input in inputs:
177
            reader = self.get_reader_for(input)
178
            self.assertRaises(
179
                (errors.UnexpectedEndOfContainerError,
180
                 errors.UnknownContainerFormatError),
181
                reader.validate)
182
183
    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.
184
        """validate raises UnknownRecordTypeError for unrecognised record
185
        types.
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)\nX")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
189
        self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
190
191
    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.
192
        """validate raises ContainerHasExcessDataError if there are any bytes
193
        after the end of the container.
194
        """
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
195
        reader = self.get_reader_for(
196
            "Bazaar pack format 1 (introduced in 0.18)\nEcrud")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
197
        self.assertRaises(
198
            errors.ContainerHasExcessDataError, reader.validate)
199
200
    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.
201
        """validate raises UnexpectedEndOfContainerError if there's no end of
202
        container marker, even if the container up to this point has been valid.
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)\n")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
206
        self.assertRaises(
207
            errors.UnexpectedEndOfContainerError, reader.validate)
208
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
209
    def test_validate_duplicate_name(self):
210
        """validate raises DuplicateRecordNameError if the same name occurs
211
        multiple times in the container.
212
        """
213
        reader = self.get_reader_for(
2506.2.10 by Andrew Bennetts
Add '(introduced in 0.18)' to pack format string.
214
            "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.
215
            "B0\nname\n\n"
216
            "B0\nname\n\n"
217
            "E")
218
        self.assertRaises(errors.DuplicateRecordNameError, reader.validate)
219
220
    def test_validate_undecodeable_name(self):
221
        """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.
222
        reader = self.get_reader_for(
223
            "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.
224
        self.assertRaises(errors.InvalidRecordError, reader.validate)
225
        
2506.3.1 by Andrew Bennetts
More progress:
226
2506.3.2 by Andrew Bennetts
Test docstring tweaks, inspired by looking over the output of jml's testdoc tool.
227
class TestBytesRecordReader(tests.TestCase):
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
228
    """Tests for reading and validating Bytes records with BytesRecordReader."""
2506.3.1 by Andrew Bennetts
More progress:
229
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
230
    def get_reader_for(self, bytes):
231
        stream = StringIO(bytes)
2506.2.9 by Aaron Bentley
Use file-like objects as container input, not callables
232
        reader = pack.BytesRecordReader(stream)
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
233
        return reader
234
2506.3.1 by Andrew Bennetts
More progress:
235
    def test_record_with_no_name(self):
236
        """Reading a Bytes record with no name returns an empty list of
237
        names.
238
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
239
        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.
240
        names, get_bytes = reader.read()
2506.3.1 by Andrew Bennetts
More progress:
241
        self.assertEqual([], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
242
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
243
244
    def test_record_with_one_name(self):
245
        """Reading a Bytes record with one name returns a list of just that
246
        name.
247
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
248
        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.
249
        names, get_bytes = reader.read()
2506.3.1 by Andrew Bennetts
More progress:
250
        self.assertEqual(['name1'], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
251
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
252
253
    def test_record_with_two_names(self):
254
        """Reading a Bytes record with two names returns a list of both names.
255
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
256
        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.
257
        names, get_bytes = reader.read()
2506.3.1 by Andrew Bennetts
More progress:
258
        self.assertEqual(['name1', 'name2'], names)
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
259
        self.assertEqual('aaaaa', get_bytes(None))
2506.3.1 by Andrew Bennetts
More progress:
260
261
    def test_invalid_length(self):
262
        """If the length-prefix is not a number, parsing raises
263
        InvalidRecordError.
264
        """
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
265
        reader = self.get_reader_for("not a number\n")
2506.3.1 by Andrew Bennetts
More progress:
266
        self.assertRaises(errors.InvalidRecordError, reader.read)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
267
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
268
    def test_early_eof(self):
269
        """Tests for premature EOF occuring during parsing Bytes records with
270
        BytesRecordReader.
271
        
272
        A incomplete container might be interrupted at any point.  The
273
        BytesRecordReader needs to cope with the input stream running out no
274
        matter where it is in the parsing process.
275
276
        In all cases, UnexpectedEndOfContainerError should be raised.
277
        """
278
        complete_record = "6\nname\n\nabcdef"
279
        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.
280
            incomplete_record = complete_record[:count]
281
            reader = self.get_reader_for(incomplete_record)
282
            # We don't use assertRaises to make diagnosing failures easier
283
            # (assertRaises doesn't allow a custom failure message).
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
284
            try:
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
285
                names, read_bytes = reader.read()
286
                read_bytes(None)
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
287
            except errors.UnexpectedEndOfContainerError:
288
                pass
289
            else:
290
                self.fail(
291
                    "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.
292
                    % (incomplete_record,))
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
293
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
294
    def test_initial_eof(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
295
        """EOF before any bytes read at all."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
296
        reader = self.get_reader_for("")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
297
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
298
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
299
    def test_eof_after_length(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
300
        """EOF after reading the length and before reading name(s)."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
301
        reader = self.get_reader_for("123\n")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
302
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
303
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
304
    def test_eof_during_name(self):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
305
        """EOF during reading a name."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
306
        reader = self.get_reader_for("123\nname")
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
307
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
308
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
309
    def test_read_invalid_name_whitespace(self):
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
310
        """Names must have no whitespace."""
311
        # A name with a space.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
312
        reader = self.get_reader_for("0\nbad name\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
313
        self.assertRaises(errors.InvalidRecordError, reader.read)
314
315
        # A name with a tab.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
316
        reader = self.get_reader_for("0\nbad\tname\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
317
        self.assertRaises(errors.InvalidRecordError, reader.read)
318
319
        # A name with a vertical tab.
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
320
        reader = self.get_reader_for("0\nbad\vname\n\n")
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
321
        self.assertRaises(errors.InvalidRecordError, reader.read)
322
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
323
    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.
324
        """Names must have no whitespace."""
325
        reader = self.get_reader_for("0\nbad name\n\n")
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
326
        self.assertRaises(errors.InvalidRecordError, reader.validate)
327
328
    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.
329
        """EOF during reading a record's prelude causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
330
        reader = self.get_reader_for("")
331
        self.assertRaises(
332
            errors.UnexpectedEndOfContainerError, reader.validate)
333
334
    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.
335
        """EOF during reading a record's body causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
336
        reader = self.get_reader_for("1\n\n")
337
        self.assertRaises(
338
            errors.UnexpectedEndOfContainerError, reader.validate)
339
340
    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.
341
        """An unparseable record length causes validate to fail."""
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
342
        reader = self.get_reader_for("\n\n")
343
        self.assertRaises(
344
            errors.InvalidRecordError, reader.validate)
345
2506.6.1 by Andrew Bennetts
Return a callable instead of a str from read, and add more validation.
346
    def test_validate_undecodeable_name(self):
347
        """Names that aren't valid UTF-8 cause validate to fail."""
348
        reader = self.get_reader_for("0\n\xcc\n\n")
349
        self.assertRaises(errors.InvalidRecordError, reader.validate)
350
351
    def test_read_max_length(self):
352
        """If the max_length passed to the callable returned by read is not
353
        None, then no more than that many bytes will be read.
354
        """
355
        reader = self.get_reader_for("6\n\nabcdef")
356
        names, get_bytes = reader.read()
357
        self.assertEqual('abc', get_bytes(3))
358
359
    def test_read_no_max_length(self):
360
        """If the max_length passed to the callable returned by read is None,
361
        then all the bytes in the record will be read.
362
        """
363
        reader = self.get_reader_for("6\n\nabcdef")
364
        names, get_bytes = reader.read()
365
        self.assertEqual('abcdef', get_bytes(None))
366
367
    def test_repeated_read_calls(self):
368
        """Repeated calls to the callable returned from BytesRecordReader.read
369
        will not read beyond the end of the record.
370
        """
371
        reader = self.get_reader_for("6\n\nabcdefB3\nnext-record\nXXX")
372
        names, get_bytes = reader.read()
373
        self.assertEqual('abcdef', get_bytes(None))
374
        self.assertEqual('', get_bytes(None))
375
        self.assertEqual('', get_bytes(99))
376
377