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