~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Patch Queue Manager
  • Date: 2015-09-30 16:43:21 UTC
  • mfrom: (6603.2.2 fix-keep-dirty)
  • Revision ID: pqm@pqm.ubuntu.com-20150930164321-ct2v2qnmvimqt8qf
(vila) Avoid associating dirty patch headers with the previous file in the
 patch. (Colin Watson)

Show diffs side-by-side

added added

removed removed

Lines of Context:
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
 
25
73
class TestContainerWriter(tests.TestCase):
26
74
 
 
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
 
27
86
    def test_construct(self):
28
87
        """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.
 
88
 
 
89
        This uses None as the output stream to show that the constructor
 
90
        doesn't try to use the output stream.
32
91
        """
33
92
        writer = pack.ContainerWriter(None)
34
93
 
35
94
    def test_begin(self):
36
95
        """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())
 
96
        self.writer.begin()
 
97
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\n')
42
98
 
43
99
    def test_zero_records_written_after_begin(self):
44
100
        """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)
 
101
        self.writer.begin()
 
102
        self.assertEqual(0, self.writer.records_written)
49
103
 
50
104
    def test_end(self):
51
105
        """The end() method writes an End Marker record."""
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())
 
106
        self.writer.begin()
 
107
        self.writer.end()
 
108
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
58
109
 
59
110
    def test_empty_end_does_not_add_a_record_to_records_written(self):
60
111
        """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)
 
112
        self.writer.begin()
 
113
        self.writer.end()
 
114
        self.assertEqual(0, self.writer.records_written)
66
115
 
67
116
    def test_non_empty_end_does_not_add_a_record_to_records_written(self):
68
117
        """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)
 
118
        self.writer.begin()
 
119
        self.writer.add_bytes_record('foo', names=[])
 
120
        self.writer.end()
 
121
        self.assertEqual(1, self.writer.records_written)
75
122
 
76
123
    def test_add_bytes_record_no_name(self):
77
124
        """Add a bytes record with no name."""
78
 
        output = StringIO()
79
 
        writer = pack.ContainerWriter(output.write)
80
 
        writer.begin()
81
 
        offset, length = writer.add_bytes_record('abc', names=[])
 
125
        self.writer.begin()
 
126
        offset, length = self.writer.add_bytes_record('abc', names=[])
82
127
        self.assertEqual((42, 7), (offset, length))
83
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
84
 
                         output.getvalue())
 
128
        self.assertOutput(
 
129
            'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
85
130
 
86
131
    def test_add_bytes_record_one_name(self):
87
132
        """Add a bytes record with one name."""
88
 
        output = StringIO()
89
 
        writer = pack.ContainerWriter(output.write)
90
 
        writer.begin()
91
 
        offset, length = writer.add_bytes_record('abc', names=[('name1', )])
 
133
        self.writer.begin()
 
134
 
 
135
        offset, length = self.writer.add_bytes_record(
 
136
            'abc', names=[('name1', )])
92
137
        self.assertEqual((42, 13), (offset, length))
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())
 
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.assertEquals([
 
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')
121
188
 
122
189
    def test_add_bytes_record_two_element_name(self):
123
190
        """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')])
 
191
        self.writer.begin()
 
192
        offset, length = self.writer.add_bytes_record(
 
193
            'abc', names=[('name1', 'name2')])
128
194
        self.assertEqual((42, 19), (offset, length))
129
 
        self.assertEqual(
 
195
        self.assertOutput(
130
196
            'Bazaar pack format 1 (introduced in 0.18)\n'
131
 
            'B3\nname1\x00name2\n\nabc',
132
 
            output.getvalue())
 
197
            'B3\nname1\x00name2\n\nabc')
133
198
 
134
199
    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=[])
 
200
        self.writer.begin()
 
201
        self.writer.add_bytes_record('abc', names=[])
 
202
        offset, length = self.writer.add_bytes_record('abc', names=[])
140
203
        self.assertEqual((49, 7), (offset, length))
141
 
        self.assertEqual(
 
204
        self.assertOutput(
142
205
            'Bazaar pack format 1 (introduced in 0.18)\n'
143
206
            'B3\n\nabc'
144
 
            'B3\n\nabc',
145
 
            output.getvalue())
 
207
            'B3\n\nabc')
146
208
 
147
209
    def test_add_bytes_record_invalid_name(self):
148
210
        """Adding a Bytes record with a name with whitespace in it raises
149
211
        InvalidRecordError.
150
212
        """
151
 
        output = StringIO()
152
 
        writer = pack.ContainerWriter(output.write)
153
 
        writer.begin()
 
213
        self.writer.begin()
154
214
        self.assertRaises(
155
215
            errors.InvalidRecordError,
156
 
            writer.add_bytes_record, 'abc', names=[('bad name', )])
 
216
            self.writer.add_bytes_record, 'abc', names=[('bad name', )])
157
217
 
158
218
    def test_add_bytes_records_add_to_records_written(self):
159
219
        """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)
 
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)
167
225
 
168
226
 
169
227
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
    """
170
234
 
171
235
    def get_reader_for(self, bytes):
172
236
        stream = StringIO(bytes)
175
239
 
176
240
    def test_construct(self):
177
241
        """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.
 
242
 
 
243
        This uses None as the output stream to show that the constructor
 
244
        doesn't try to use the input stream.
181
245
        """
182
246
        reader = pack.ContainerReader(None)
183
247
 
213
277
 
214
278
    def test_container_with_one_unnamed_record(self):
215
279
        """Read a container with one Bytes record.
216
 
        
 
280
 
217
281
        Parsing Bytes records is more thoroughly exercised by
218
282
        TestBytesRecordReader.  This test is here to ensure that
219
283
        ContainerReader's integration with BytesRecordReader is working.
228
292
 
229
293
    def test_validate_empty_container(self):
230
294
        """validate does not raise an error for a container with no records."""
231
 
        reader = self.get_reader_for("Bazaar pack format 1 (introduced in 0.18)\nE")
 
295
        reader = self.get_reader_for(
 
296
            "Bazaar pack format 1 (introduced in 0.18)\nE")
232
297
        # No exception raised
233
298
        reader.validate()
234
299
 
296
361
        reader = self.get_reader_for(
297
362
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
298
363
        self.assertRaises(errors.InvalidRecordError, reader.validate)
299
 
        
 
364
 
300
365
 
301
366
class TestBytesRecordReader(tests.TestCase):
302
 
    """Tests for reading and validating Bytes records with BytesRecordReader."""
 
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
    """
303
374
 
304
375
    def get_reader_for(self, bytes):
305
376
        stream = StringIO(bytes)
349
420
    def test_early_eof(self):
350
421
        """Tests for premature EOF occuring during parsing Bytes records with
351
422
        BytesRecordReader.
352
 
        
 
423
 
353
424
        A incomplete container might be interrupted at any point.  The
354
425
        BytesRecordReader needs to cope with the input stream running out no
355
426
        matter where it is in the parsing process.
482
553
    """Tests of the ReadVFile class.
483
554
 
484
555
    Error cases are deliberately undefined: this code adapts the underlying
485
 
    transport interface to a single 'streaming read' interface as 
 
556
    transport interface to a single 'streaming read' interface as
486
557
    ContainerReader needs.
487
558
    """
488
559
 
523
594
        results.append(f.readline())
524
595
        results.append(f.read(4))
525
596
        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())