~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Alexander Belchenko
  • Date: 2007-09-25 22:13:08 UTC
  • mfrom: (2831.8.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 2867.
  • Revision ID: bialix@ukr.net-20070925221308-aqaqq1u2qv6kpl2z
merge with bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2009, 2011, 2012, 2016 Canonical Ltd
 
1
# Copyright (C) 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Tests for bzrlib.pack."""
18
18
 
22
22
from bzrlib import pack, errors, tests
23
23
 
24
24
 
25
 
class TestContainerSerialiser(tests.TestCase):
26
 
    """Tests for the ContainerSerialiser class."""
27
 
 
28
 
    def test_construct(self):
29
 
        """Test constructing a ContainerSerialiser."""
30
 
        pack.ContainerSerialiser()
31
 
 
32
 
    def test_begin(self):
33
 
        serialiser = pack.ContainerSerialiser()
34
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
35
 
                         serialiser.begin())
36
 
 
37
 
    def test_end(self):
38
 
        serialiser = pack.ContainerSerialiser()
39
 
        self.assertEqual('E', serialiser.end())
40
 
 
41
 
    def test_bytes_record_no_name(self):
42
 
        serialiser = pack.ContainerSerialiser()
43
 
        record = serialiser.bytes_record('bytes', [])
44
 
        self.assertEqual('B5\n\nbytes', record)
45
 
 
46
 
    def test_bytes_record_one_name_with_one_part(self):
47
 
        serialiser = pack.ContainerSerialiser()
48
 
        record = serialiser.bytes_record('bytes', [('name',)])
49
 
        self.assertEqual('B5\nname\n\nbytes', record)
50
 
 
51
 
    def test_bytes_record_one_name_with_two_parts(self):
52
 
        serialiser = pack.ContainerSerialiser()
53
 
        record = serialiser.bytes_record('bytes', [('part1', 'part2')])
54
 
        self.assertEqual('B5\npart1\x00part2\n\nbytes', record)
55
 
 
56
 
    def test_bytes_record_two_names(self):
57
 
        serialiser = pack.ContainerSerialiser()
58
 
        record = serialiser.bytes_record('bytes', [('name1',), ('name2',)])
59
 
        self.assertEqual('B5\nname1\nname2\n\nbytes', record)
60
 
 
61
 
    def test_bytes_record_whitespace_in_name_part(self):
62
 
        serialiser = pack.ContainerSerialiser()
63
 
        self.assertRaises(
64
 
            errors.InvalidRecordError,
65
 
            serialiser.bytes_record, 'bytes', [('bad name',)])
66
 
 
67
 
    def test_bytes_record_header(self):
68
 
        serialiser = pack.ContainerSerialiser()
69
 
        record = serialiser.bytes_header(32, [('name1',), ('name2',)])
70
 
        self.assertEqual('B32\nname1\nname2\n\n', record)
71
 
 
72
 
 
73
25
class TestContainerWriter(tests.TestCase):
74
26
 
75
 
    def setUp(self):
76
 
        super(TestContainerWriter, self).setUp()
77
 
        self.output = StringIO()
78
 
        self.writer = pack.ContainerWriter(self.output.write)
79
 
 
80
 
    def assertOutput(self, expected_output):
81
 
        """Assert that the output of self.writer ContainerWriter is equal to
82
 
        expected_output.
83
 
        """
84
 
        self.assertEqual(expected_output, self.output.getvalue())
85
 
 
86
27
    def test_construct(self):
87
28
        """Test constructing a ContainerWriter.
88
 
 
89
 
        This uses None as the output stream to show that the constructor
90
 
        doesn't try to use the output stream.
 
29
        
 
30
        This uses None as the output stream to show that the constructor doesn't
 
31
        try to use the output stream.
91
32
        """
92
33
        writer = pack.ContainerWriter(None)
93
34
 
94
35
    def test_begin(self):
95
36
        """The begin() method writes the container format marker line."""
96
 
        self.writer.begin()
97
 
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\n')
 
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())
98
42
 
99
43
    def test_zero_records_written_after_begin(self):
100
44
        """After begin is written, 0 records have been written."""
101
 
        self.writer.begin()
102
 
        self.assertEqual(0, self.writer.records_written)
 
45
        output = StringIO()
 
46
        writer = pack.ContainerWriter(output.write)
 
47
        writer.begin()
 
48
        self.assertEqual(0, writer.records_written)
103
49
 
104
50
    def test_end(self):
105
51
        """The end() method writes an End Marker record."""
106
 
        self.writer.begin()
107
 
        self.writer.end()
108
 
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
 
52
        output = StringIO()
 
53
        writer = pack.ContainerWriter(output.write)
 
54
        writer.begin()
 
55
        writer.end()
 
56
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
 
57
                         output.getvalue())
109
58
 
110
59
    def test_empty_end_does_not_add_a_record_to_records_written(self):
111
60
        """The end() method does not count towards the records written."""
112
 
        self.writer.begin()
113
 
        self.writer.end()
114
 
        self.assertEqual(0, self.writer.records_written)
 
61
        output = StringIO()
 
62
        writer = pack.ContainerWriter(output.write)
 
63
        writer.begin()
 
64
        writer.end()
 
65
        self.assertEqual(0, writer.records_written)
115
66
 
116
67
    def test_non_empty_end_does_not_add_a_record_to_records_written(self):
117
68
        """The end() method does not count towards the records written."""
118
 
        self.writer.begin()
119
 
        self.writer.add_bytes_record('foo', names=[])
120
 
        self.writer.end()
121
 
        self.assertEqual(1, self.writer.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)
122
75
 
123
76
    def test_add_bytes_record_no_name(self):
124
77
        """Add a bytes record with no name."""
125
 
        self.writer.begin()
126
 
        offset, length = self.writer.add_bytes_record('abc', names=[])
 
78
        output = StringIO()
 
79
        writer = pack.ContainerWriter(output.write)
 
80
        writer.begin()
 
81
        offset, length = writer.add_bytes_record('abc', names=[])
127
82
        self.assertEqual((42, 7), (offset, length))
128
 
        self.assertOutput(
129
 
            'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
 
83
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
 
84
                         output.getvalue())
130
85
 
131
86
    def test_add_bytes_record_one_name(self):
132
87
        """Add a bytes record with one name."""
133
 
        self.writer.begin()
134
 
 
135
 
        offset, length = self.writer.add_bytes_record(
136
 
            'abc', names=[('name1', )])
 
88
        output = StringIO()
 
89
        writer = pack.ContainerWriter(output.write)
 
90
        writer.begin()
 
91
        offset, length = writer.add_bytes_record('abc', names=[('name1', )])
137
92
        self.assertEqual((42, 13), (offset, length))
138
 
        self.assertOutput(
139
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
140
 
            'B3\nname1\n\nabc')
141
 
 
142
 
    def test_add_bytes_record_split_writes(self):
143
 
        """Write a large record which does multiple IOs"""
144
 
 
145
 
        writes = []
146
 
        real_write = self.writer.write_func
147
 
 
148
 
        def record_writes(bytes):
149
 
            writes.append(bytes)
150
 
            return real_write(bytes)
151
 
 
152
 
        self.writer.write_func = record_writes
153
 
        self.writer._JOIN_WRITES_THRESHOLD = 2
154
 
 
155
 
        self.writer.begin()
156
 
        offset, length = self.writer.add_bytes_record(
157
 
            'abcabc', names=[('name1', )])
158
 
        self.assertEqual((42, 16), (offset, length))
159
 
        self.assertOutput(
160
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
161
 
            'B6\nname1\n\nabcabc')
162
 
 
163
 
        self.assertEqual([
164
 
            'Bazaar pack format 1 (introduced in 0.18)\n',
165
 
            'B6\nname1\n\n',
166
 
            'abcabc'],
167
 
            writes)
168
 
 
169
 
    def test_add_bytes_record_two_names(self):
170
 
        """Add a bytes record with two names."""
171
 
        self.writer.begin()
172
 
        offset, length = self.writer.add_bytes_record(
173
 
            'abc', names=[('name1', ), ('name2', )])
174
 
        self.assertEqual((42, 19), (offset, length))
175
 
        self.assertOutput(
176
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
177
 
            'B3\nname1\nname2\n\nabc')
178
 
 
179
 
    def test_add_bytes_record_two_names(self):
180
 
        """Add a bytes record with two names."""
181
 
        self.writer.begin()
182
 
        offset, length = self.writer.add_bytes_record(
183
 
            'abc', names=[('name1', ), ('name2', )])
184
 
        self.assertEqual((42, 19), (offset, length))
185
 
        self.assertOutput(
186
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
187
 
            'B3\nname1\nname2\n\nabc')
 
93
        self.assertEqual(
 
94
            'Bazaar pack format 1 (introduced in 0.18)\n'
 
95
            'B3\nname1\n\nabc',
 
96
            output.getvalue())
 
97
 
 
98
    def test_add_bytes_record_two_names(self):
 
99
        """Add a bytes record with two names."""
 
100
        output = StringIO()
 
101
        writer = pack.ContainerWriter(output.write)
 
102
        writer.begin()
 
103
        offset, length = writer.add_bytes_record('abc', names=[('name1', ), ('name2', )])
 
104
        self.assertEqual((42, 19), (offset, length))
 
105
        self.assertEqual(
 
106
            'Bazaar pack format 1 (introduced in 0.18)\n'
 
107
            'B3\nname1\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())
188
121
 
189
122
    def test_add_bytes_record_two_element_name(self):
190
123
        """Add a bytes record with a two-element name."""
191
 
        self.writer.begin()
192
 
        offset, length = self.writer.add_bytes_record(
193
 
            'abc', names=[('name1', 'name2')])
 
124
        output = StringIO()
 
125
        writer = pack.ContainerWriter(output.write)
 
126
        writer.begin()
 
127
        offset, length = writer.add_bytes_record('abc', names=[('name1', 'name2')])
194
128
        self.assertEqual((42, 19), (offset, length))
195
 
        self.assertOutput(
 
129
        self.assertEqual(
196
130
            'Bazaar pack format 1 (introduced in 0.18)\n'
197
 
            'B3\nname1\x00name2\n\nabc')
 
131
            'B3\nname1\x00name2\n\nabc',
 
132
            output.getvalue())
198
133
 
199
134
    def test_add_second_bytes_record_gets_higher_offset(self):
200
 
        self.writer.begin()
201
 
        self.writer.add_bytes_record('abc', names=[])
202
 
        offset, length = self.writer.add_bytes_record('abc', names=[])
 
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=[])
203
140
        self.assertEqual((49, 7), (offset, length))
204
 
        self.assertOutput(
 
141
        self.assertEqual(
205
142
            'Bazaar pack format 1 (introduced in 0.18)\n'
206
143
            'B3\n\nabc'
207
 
            'B3\n\nabc')
 
144
            'B3\n\nabc',
 
145
            output.getvalue())
208
146
 
209
147
    def test_add_bytes_record_invalid_name(self):
210
148
        """Adding a Bytes record with a name with whitespace in it raises
211
149
        InvalidRecordError.
212
150
        """
213
 
        self.writer.begin()
 
151
        output = StringIO()
 
152
        writer = pack.ContainerWriter(output.write)
 
153
        writer.begin()
214
154
        self.assertRaises(
215
155
            errors.InvalidRecordError,
216
 
            self.writer.add_bytes_record, 'abc', names=[('bad name', )])
 
156
            writer.add_bytes_record, 'abc', names=[('bad name', )])
217
157
 
218
158
    def test_add_bytes_records_add_to_records_written(self):
219
159
        """Adding a Bytes record increments the records_written counter."""
220
 
        self.writer.begin()
221
 
        self.writer.add_bytes_record('foo', names=[])
222
 
        self.assertEqual(1, self.writer.records_written)
223
 
        self.writer.add_bytes_record('foo', names=[])
224
 
        self.assertEqual(2, self.writer.records_written)
 
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)
225
167
 
226
168
 
227
169
class TestContainerReader(tests.TestCase):
228
 
    """Tests for the ContainerReader.
229
 
 
230
 
    The ContainerReader reads format 1 containers, so these tests explicitly
231
 
    test how it reacts to format 1 data.  If a new version of the format is
232
 
    added, then separate tests for that format should be added.
233
 
    """
234
170
 
235
171
    def get_reader_for(self, bytes):
236
172
        stream = StringIO(bytes)
239
175
 
240
176
    def test_construct(self):
241
177
        """Test constructing a ContainerReader.
242
 
 
243
 
        This uses None as the output stream to show that the constructor
244
 
        doesn't try to use the input stream.
 
178
        
 
179
        This uses None as the output stream to show that the constructor doesn't
 
180
        try to use the input stream.
245
181
        """
246
182
        reader = pack.ContainerReader(None)
247
183
 
277
213
 
278
214
    def test_container_with_one_unnamed_record(self):
279
215
        """Read a container with one Bytes record.
280
 
 
 
216
        
281
217
        Parsing Bytes records is more thoroughly exercised by
282
218
        TestBytesRecordReader.  This test is here to ensure that
283
219
        ContainerReader's integration with BytesRecordReader is working.
292
228
 
293
229
    def test_validate_empty_container(self):
294
230
        """validate does not raise an error for a container with no records."""
295
 
        reader = self.get_reader_for(
296
 
            "Bazaar pack format 1 (introduced in 0.18)\nE")
 
231
        reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
297
232
        # No exception raised
298
233
        reader.validate()
299
234
 
361
296
        reader = self.get_reader_for(
362
297
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
363
298
        self.assertRaises(errors.InvalidRecordError, reader.validate)
364
 
 
 
299
        
365
300
 
366
301
class TestBytesRecordReader(tests.TestCase):
367
 
    """Tests for reading and validating Bytes records with
368
 
    BytesRecordReader.
369
 
 
370
 
    Like TestContainerReader, this explicitly tests the reading of format 1
371
 
    data.  If a new version of the format is added, then a separate set of
372
 
    tests for reading that format should be added.
373
 
    """
 
302
    """Tests for reading and validating Bytes records with BytesRecordReader."""
374
303
 
375
304
    def get_reader_for(self, bytes):
376
305
        stream = StringIO(bytes)
420
349
    def test_early_eof(self):
421
350
        """Tests for premature EOF occuring during parsing Bytes records with
422
351
        BytesRecordReader.
423
 
 
 
352
        
424
353
        A incomplete container might be interrupted at any point.  The
425
354
        BytesRecordReader needs to cope with the input stream running out no
426
355
        matter where it is in the parsing process.
553
482
    """Tests of the ReadVFile class.
554
483
 
555
484
    Error cases are deliberately undefined: this code adapts the underlying
556
 
    transport interface to a single 'streaming read' interface as
 
485
    transport interface to a single 'streaming read' interface as 
557
486
    ContainerReader needs.
558
487
    """
559
488
 
594
523
        results.append(f.readline())
595
524
        results.append(f.read(4))
596
525
        self.assertEqual(['0', '\n', '2\n4\n'], results)
597
 
 
598
 
 
599
 
class PushParserTestCase(tests.TestCase):
600
 
    """Base class for TestCases involving ContainerPushParser."""
601
 
 
602
 
    def make_parser_expecting_record_type(self):
603
 
        parser = pack.ContainerPushParser()
604
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\n")
605
 
        return parser
606
 
 
607
 
    def make_parser_expecting_bytes_record(self):
608
 
        parser = pack.ContainerPushParser()
609
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\nB")
610
 
        return parser
611
 
 
612
 
    def assertRecordParsing(self, expected_record, bytes):
613
 
        """Assert that 'bytes' is parsed as a given bytes record.
614
 
 
615
 
        :param expected_record: A tuple of (names, bytes).
616
 
        """
617
 
        parser = self.make_parser_expecting_bytes_record()
618
 
        parser.accept_bytes(bytes)
619
 
        parsed_records = parser.read_pending_records()
620
 
        self.assertEqual([expected_record], parsed_records)
621
 
 
622
 
 
623
 
class TestContainerPushParser(PushParserTestCase):
624
 
    """Tests for ContainerPushParser.
625
 
 
626
 
    The ContainerPushParser reads format 1 containers, so these tests
627
 
    explicitly test how it reacts to format 1 data.  If a new version of the
628
 
    format is added, then separate tests for that format should be added.
629
 
    """
630
 
 
631
 
    def test_construct(self):
632
 
        """ContainerPushParser can be constructed."""
633
 
        pack.ContainerPushParser()
634
 
 
635
 
    def test_multiple_records_at_once(self):
636
 
        """If multiple records worth of data are fed to the parser in one
637
 
        string, the parser will correctly parse all the records.
638
 
 
639
 
        (A naive implementation might stop after parsing the first record.)
640
 
        """
641
 
        parser = self.make_parser_expecting_record_type()
642
 
        parser.accept_bytes("B5\nname1\n\nbody1B5\nname2\n\nbody2")
643
 
        self.assertEqual(
644
 
            [([('name1',)], 'body1'), ([('name2',)], 'body2')],
645
 
            parser.read_pending_records())
646
 
 
647
 
    def test_multiple_empty_records_at_once(self):
648
 
        """If multiple empty records worth of data are fed to the parser in one
649
 
        string, the parser will correctly parse all the records.
650
 
 
651
 
        (A naive implementation might stop after parsing the first empty
652
 
        record, because the buffer size had not changed.)
653
 
        """
654
 
        parser = self.make_parser_expecting_record_type()
655
 
        parser.accept_bytes("B0\nname1\n\nB0\nname2\n\n")
656
 
        self.assertEqual(
657
 
            [([('name1',)], ''), ([('name2',)], '')],
658
 
            parser.read_pending_records())
659
 
 
660
 
 
661
 
class TestContainerPushParserBytesParsing(PushParserTestCase):
662
 
    """Tests for reading Bytes records with ContainerPushParser.
663
 
 
664
 
    The ContainerPushParser reads format 1 containers, so these tests
665
 
    explicitly test how it reacts to format 1 data.  If a new version of the
666
 
    format is added, then separate tests for that format should be added.
667
 
    """
668
 
 
669
 
    def test_record_with_no_name(self):
670
 
        """Reading a Bytes record with no name returns an empty list of
671
 
        names.
672
 
        """
673
 
        self.assertRecordParsing(([], 'aaaaa'), "5\n\naaaaa")
674
 
 
675
 
    def test_record_with_one_name(self):
676
 
        """Reading a Bytes record with one name returns a list of just that
677
 
        name.
678
 
        """
679
 
        self.assertRecordParsing(
680
 
            ([('name1', )], 'aaaaa'),
681
 
            "5\nname1\n\naaaaa")
682
 
 
683
 
    def test_record_with_two_names(self):
684
 
        """Reading a Bytes record with two names returns a list of both names.
685
 
        """
686
 
        self.assertRecordParsing(
687
 
            ([('name1', ), ('name2', )], 'aaaaa'),
688
 
            "5\nname1\nname2\n\naaaaa")
689
 
 
690
 
    def test_record_with_two_part_names(self):
691
 
        """Reading a Bytes record with a two_part name reads both."""
692
 
        self.assertRecordParsing(
693
 
            ([('name1', 'name2')], 'aaaaa'),
694
 
            "5\nname1\x00name2\n\naaaaa")
695
 
 
696
 
    def test_invalid_length(self):
697
 
        """If the length-prefix is not a number, parsing raises
698
 
        InvalidRecordError.
699
 
        """
700
 
        parser = self.make_parser_expecting_bytes_record()
701
 
        self.assertRaises(
702
 
            errors.InvalidRecordError, parser.accept_bytes, "not a number\n")
703
 
 
704
 
    def test_incomplete_record(self):
705
 
        """If the bytes seen so far don't form a complete record, then there
706
 
        will be nothing returned by read_pending_records.
707
 
        """
708
 
        parser = self.make_parser_expecting_bytes_record()
709
 
        parser.accept_bytes("5\n\nabcd")
710
 
        self.assertEqual([], parser.read_pending_records())
711
 
 
712
 
    def test_accept_nothing(self):
713
 
        """The edge case of parsing an empty string causes no error."""
714
 
        parser = self.make_parser_expecting_bytes_record()
715
 
        parser.accept_bytes("")
716
 
 
717
 
    def assertInvalidRecord(self, bytes):
718
 
        """Assert that parsing the given bytes will raise an
719
 
        InvalidRecordError.
720
 
        """
721
 
        parser = self.make_parser_expecting_bytes_record()
722
 
        self.assertRaises(
723
 
            errors.InvalidRecordError, parser.accept_bytes, bytes)
724
 
 
725
 
    def test_read_invalid_name_whitespace(self):
726
 
        """Names must have no whitespace."""
727
 
        # A name with a space.
728
 
        self.assertInvalidRecord("0\nbad name\n\n")
729
 
 
730
 
        # A name with a tab.
731
 
        self.assertInvalidRecord("0\nbad\tname\n\n")
732
 
 
733
 
        # A name with a vertical tab.
734
 
        self.assertInvalidRecord("0\nbad\vname\n\n")
735
 
 
736
 
    def test_repeated_read_pending_records(self):
737
 
        """read_pending_records will not return the same record twice."""
738
 
        parser = self.make_parser_expecting_bytes_record()
739
 
        parser.accept_bytes("6\n\nabcdef")
740
 
        self.assertEqual([([], 'abcdef')], parser.read_pending_records())
741
 
        self.assertEqual([], parser.read_pending_records())