~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Martin Pool
  • Date: 2005-07-18 13:38:13 UTC
  • Revision ID: mbp@sourcefrog.net-20050718133813-4343f0cde39537de
- refactor member names in Weave code

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
17
 
"""Tests for bzrlib.pack."""
18
 
 
19
 
 
20
 
from cStringIO import StringIO
21
 
 
22
 
from bzrlib import pack, errors, tests
23
 
 
24
 
 
25
 
class TestContainerWriter(tests.TestCase):
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
 
        """
33
 
        writer = pack.ContainerWriter(None)
34
 
 
35
 
    def test_begin(self):
36
 
        """The begin() method writes the container format marker line."""
37
 
        output = StringIO()
38
 
        writer = pack.ContainerWriter(output.write)
39
 
        writer.begin()
40
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
41
 
                         output.getvalue())
42
 
 
43
 
    def test_end(self):
44
 
        """The end() method writes an End Marker record."""
45
 
        output = StringIO()
46
 
        writer = pack.ContainerWriter(output.write)
47
 
        writer.begin()
48
 
        writer.end()
49
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
50
 
                         output.getvalue())
51
 
 
52
 
    def test_add_bytes_record_no_name(self):
53
 
        """Add a bytes record with no name."""
54
 
        output = StringIO()
55
 
        writer = pack.ContainerWriter(output.write)
56
 
        writer.begin()
57
 
        writer.add_bytes_record('abc', names=[])
58
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
59
 
                         output.getvalue())
60
 
 
61
 
    def test_add_bytes_record_one_name(self):
62
 
        """Add a bytes record with one name."""
63
 
        output = StringIO()
64
 
        writer = pack.ContainerWriter(output.write)
65
 
        writer.begin()
66
 
        writer.add_bytes_record('abc', names=['name1'])
67
 
        self.assertEqual(
68
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
69
 
            'B3\nname1\n\nabc',
70
 
            output.getvalue())
71
 
 
72
 
    def test_add_bytes_record_two_names(self):
73
 
        """Add a bytes record with two names."""
74
 
        output = StringIO()
75
 
        writer = pack.ContainerWriter(output.write)
76
 
        writer.begin()
77
 
        writer.add_bytes_record('abc', names=['name1', 'name2'])
78
 
        self.assertEqual(
79
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
80
 
            'B3\nname1\nname2\n\nabc',
81
 
            output.getvalue())
82
 
 
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
 
 
94
 
 
95
 
class TestContainerReader(tests.TestCase):
96
 
 
97
 
    def get_reader_for(self, bytes):
98
 
        stream = StringIO(bytes)
99
 
        reader = pack.ContainerReader(stream)
100
 
        return reader
101
 
 
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
 
        """
108
 
        reader = pack.ContainerReader(None)
109
 
 
110
 
    def test_empty_container(self):
111
 
        """Read an empty container."""
112
 
        reader = self.get_reader_for(
113
 
            "Bazaar pack format 1 (introduced in 0.18)\nE")
114
 
        self.assertEqual([], list(reader.iter_records()))
115
 
 
116
 
    def test_unknown_format(self):
117
 
        """Unrecognised container formats raise UnknownContainerFormatError."""
118
 
        reader = self.get_reader_for("unknown format\n")
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
 
        """
126
 
        reader = self.get_reader_for(
127
 
            "Bazaar pack format 1 (introduced in 0.18)\n")
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."""
134
 
        reader = self.get_reader_for(
135
 
            "Bazaar pack format 1 (introduced in 0.18)\nX")
136
 
        iterator = reader.iter_records()
137
 
        self.assertRaises(
138
 
            errors.UnknownRecordTypeError, iterator.next)
139
 
 
140
 
    def test_container_with_one_unnamed_record(self):
141
 
        """Read a container with one Bytes record.
142
 
        
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.
146
 
        """
147
 
        reader = self.get_reader_for(
148
 
            "Bazaar pack format 1 (introduced in 0.18)\nB5\n\naaaaaE")
149
 
        expected_records = [([], 'aaaaa')]
150
 
        self.assertEqual(
151
 
            expected_records,
152
 
            [(names, read_bytes(None))
153
 
             for (names, read_bytes) in reader.iter_records()])
154
 
 
155
 
    def test_validate_empty_container(self):
156
 
        """validate does not raise an error for a container with no records."""
157
 
        reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
158
 
        # No exception raised
159
 
        reader.validate()
160
 
 
161
 
    def test_validate_non_empty_valid_container(self):
162
 
        """validate does not raise an error for a container with a valid record.
163
 
        """
164
 
        reader = self.get_reader_for(
165
 
            "Bazaar pack format 1 (introduced in 0.18)\nB3\nname\n\nabcE")
166
 
        # No exception raised
167
 
        reader.validate()
168
 
 
169
 
    def test_validate_bad_format(self):
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
 
        """
175
 
        inputs = ["", "x", "Bazaar pack format 1 (introduced in 0.18)", "bad\n"]
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):
184
 
        """validate raises UnknownRecordTypeError for unrecognised record
185
 
        types.
186
 
        """
187
 
        reader = self.get_reader_for(
188
 
            "Bazaar pack format 1 (introduced in 0.18)\nX")
189
 
        self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
190
 
 
191
 
    def test_validate_data_after_end_marker(self):
192
 
        """validate raises ContainerHasExcessDataError if there are any bytes
193
 
        after the end of the container.
194
 
        """
195
 
        reader = self.get_reader_for(
196
 
            "Bazaar pack format 1 (introduced in 0.18)\nEcrud")
197
 
        self.assertRaises(
198
 
            errors.ContainerHasExcessDataError, reader.validate)
199
 
 
200
 
    def test_validate_no_end_marker(self):
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
 
        """
204
 
        reader = self.get_reader_for(
205
 
            "Bazaar pack format 1 (introduced in 0.18)\n")
206
 
        self.assertRaises(
207
 
            errors.UnexpectedEndOfContainerError, reader.validate)
208
 
 
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(
214
 
            "Bazaar pack format 1 (introduced in 0.18)\n"
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."""
222
 
        reader = self.get_reader_for(
223
 
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
224
 
        self.assertRaises(errors.InvalidRecordError, reader.validate)
225
 
        
226
 
 
227
 
class TestBytesRecordReader(tests.TestCase):
228
 
    """Tests for reading and validating Bytes records with BytesRecordReader."""
229
 
 
230
 
    def get_reader_for(self, bytes):
231
 
        stream = StringIO(bytes)
232
 
        reader = pack.BytesRecordReader(stream)
233
 
        return reader
234
 
 
235
 
    def test_record_with_no_name(self):
236
 
        """Reading a Bytes record with no name returns an empty list of
237
 
        names.
238
 
        """
239
 
        reader = self.get_reader_for("5\n\naaaaa")
240
 
        names, get_bytes = reader.read()
241
 
        self.assertEqual([], names)
242
 
        self.assertEqual('aaaaa', get_bytes(None))
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
 
        """
248
 
        reader = self.get_reader_for("5\nname1\n\naaaaa")
249
 
        names, get_bytes = reader.read()
250
 
        self.assertEqual(['name1'], names)
251
 
        self.assertEqual('aaaaa', get_bytes(None))
252
 
 
253
 
    def test_record_with_two_names(self):
254
 
        """Reading a Bytes record with two names returns a list of both names.
255
 
        """
256
 
        reader = self.get_reader_for("5\nname1\nname2\n\naaaaa")
257
 
        names, get_bytes = reader.read()
258
 
        self.assertEqual(['name1', 'name2'], names)
259
 
        self.assertEqual('aaaaa', get_bytes(None))
260
 
 
261
 
    def test_invalid_length(self):
262
 
        """If the length-prefix is not a number, parsing raises
263
 
        InvalidRecordError.
264
 
        """
265
 
        reader = self.get_reader_for("not a number\n")
266
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
267
 
 
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)):
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).
284
 
            try:
285
 
                names, read_bytes = reader.read()
286
 
                read_bytes(None)
287
 
            except errors.UnexpectedEndOfContainerError:
288
 
                pass
289
 
            else:
290
 
                self.fail(
291
 
                    "UnexpectedEndOfContainerError not raised when parsing %r"
292
 
                    % (incomplete_record,))
293
 
 
294
 
    def test_initial_eof(self):
295
 
        """EOF before any bytes read at all."""
296
 
        reader = self.get_reader_for("")
297
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
298
 
 
299
 
    def test_eof_after_length(self):
300
 
        """EOF after reading the length and before reading name(s)."""
301
 
        reader = self.get_reader_for("123\n")
302
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
303
 
 
304
 
    def test_eof_during_name(self):
305
 
        """EOF during reading a name."""
306
 
        reader = self.get_reader_for("123\nname")
307
 
        self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
308
 
 
309
 
    def test_read_invalid_name_whitespace(self):
310
 
        """Names must have no whitespace."""
311
 
        # A name with a space.
312
 
        reader = self.get_reader_for("0\nbad name\n\n")
313
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
314
 
 
315
 
        # A name with a tab.
316
 
        reader = self.get_reader_for("0\nbad\tname\n\n")
317
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
318
 
 
319
 
        # A name with a vertical tab.
320
 
        reader = self.get_reader_for("0\nbad\vname\n\n")
321
 
        self.assertRaises(errors.InvalidRecordError, reader.read)
322
 
 
323
 
    def test_validate_whitespace_in_name(self):
324
 
        """Names must have no whitespace."""
325
 
        reader = self.get_reader_for("0\nbad name\n\n")
326
 
        self.assertRaises(errors.InvalidRecordError, reader.validate)
327
 
 
328
 
    def test_validate_interrupted_prelude(self):
329
 
        """EOF during reading a record's prelude causes validate to fail."""
330
 
        reader = self.get_reader_for("")
331
 
        self.assertRaises(
332
 
            errors.UnexpectedEndOfContainerError, reader.validate)
333
 
 
334
 
    def test_validate_interrupted_body(self):
335
 
        """EOF during reading a record's body causes validate to fail."""
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):
341
 
        """An unparseable record length causes validate to fail."""
342
 
        reader = self.get_reader_for("\n\n")
343
 
        self.assertRaises(
344
 
            errors.InvalidRecordError, reader.validate)
345
 
 
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