/brz/remove-bazaar

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