~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Robert Collins
  • Date: 2007-10-23 22:14:32 UTC
  • mto: (2592.6.3 repository)
  • mto: This revision was merged to the branch mainline in revision 2967.
  • Revision ID: robertc@robertcollins.net-20071023221432-j8zndh1oiegql3cu
* Commit updates the state of the working tree via a delta rather than
  supplying entirely new basis trees. For commit of a single specified file
  this reduces the wall clock time for commit by roughly a 30%.
  (Robert Collins, Martin Pool)

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., 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
 
 
68
25
class TestContainerWriter(tests.TestCase):
69
26
 
70
 
    def setUp(self):
71
 
        tests.TestCase.setUp(self)
72
 
        self.output = StringIO()
73
 
        self.writer = pack.ContainerWriter(self.output.write)
74
 
 
75
 
    def assertOutput(self, expected_output):
76
 
        """Assert that the output of self.writer ContainerWriter is equal to
77
 
        expected_output.
78
 
        """
79
 
        self.assertEqual(expected_output, self.output.getvalue())
80
 
 
81
27
    def test_construct(self):
82
28
        """Test constructing a ContainerWriter.
83
 
 
84
 
        This uses None as the output stream to show that the constructor
85
 
        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.
86
32
        """
87
33
        writer = pack.ContainerWriter(None)
88
34
 
89
35
    def test_begin(self):
90
36
        """The begin() method writes the container format marker line."""
91
 
        self.writer.begin()
92
 
        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())
93
42
 
94
43
    def test_zero_records_written_after_begin(self):
95
44
        """After begin is written, 0 records have been written."""
96
 
        self.writer.begin()
97
 
        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)
98
49
 
99
50
    def test_end(self):
100
51
        """The end() method writes an End Marker record."""
101
 
        self.writer.begin()
102
 
        self.writer.end()
103
 
        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())
104
58
 
105
59
    def test_empty_end_does_not_add_a_record_to_records_written(self):
106
60
        """The end() method does not count towards the records written."""
107
 
        self.writer.begin()
108
 
        self.writer.end()
109
 
        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)
110
66
 
111
67
    def test_non_empty_end_does_not_add_a_record_to_records_written(self):
112
68
        """The end() method does not count towards the records written."""
113
 
        self.writer.begin()
114
 
        self.writer.add_bytes_record('foo', names=[])
115
 
        self.writer.end()
116
 
        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)
117
75
 
118
76
    def test_add_bytes_record_no_name(self):
119
77
        """Add a bytes record with no name."""
120
 
        self.writer.begin()
121
 
        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=[])
122
82
        self.assertEqual((42, 7), (offset, length))
123
 
        self.assertOutput(
124
 
            '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())
125
85
 
126
86
    def test_add_bytes_record_one_name(self):
127
87
        """Add a bytes record with one name."""
128
 
        self.writer.begin()
129
 
        offset, length = self.writer.add_bytes_record(
130
 
            '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', )])
131
92
        self.assertEqual((42, 13), (offset, length))
132
 
        self.assertOutput(
133
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
134
 
            'B3\nname1\n\nabc')
135
 
 
136
 
    def test_add_bytes_record_two_names(self):
137
 
        """Add a bytes record with two names."""
138
 
        self.writer.begin()
139
 
        offset, length = self.writer.add_bytes_record(
140
 
            'abc', names=[('name1', ), ('name2', )])
141
 
        self.assertEqual((42, 19), (offset, length))
142
 
        self.assertOutput(
143
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
144
 
            'B3\nname1\nname2\n\nabc')
145
 
 
146
 
    def test_add_bytes_record_two_names(self):
147
 
        """Add a bytes record with two names."""
148
 
        self.writer.begin()
149
 
        offset, length = self.writer.add_bytes_record(
150
 
            'abc', names=[('name1', ), ('name2', )])
151
 
        self.assertEqual((42, 19), (offset, length))
152
 
        self.assertOutput(
153
 
            'Bazaar pack format 1 (introduced in 0.18)\n'
154
 
            '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())
155
121
 
156
122
    def test_add_bytes_record_two_element_name(self):
157
123
        """Add a bytes record with a two-element name."""
158
 
        self.writer.begin()
159
 
        offset, length = self.writer.add_bytes_record(
160
 
            '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')])
161
128
        self.assertEqual((42, 19), (offset, length))
162
 
        self.assertOutput(
 
129
        self.assertEqual(
163
130
            'Bazaar pack format 1 (introduced in 0.18)\n'
164
 
            'B3\nname1\x00name2\n\nabc')
 
131
            'B3\nname1\x00name2\n\nabc',
 
132
            output.getvalue())
165
133
 
166
134
    def test_add_second_bytes_record_gets_higher_offset(self):
167
 
        self.writer.begin()
168
 
        self.writer.add_bytes_record('abc', names=[])
169
 
        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=[])
170
140
        self.assertEqual((49, 7), (offset, length))
171
 
        self.assertOutput(
 
141
        self.assertEqual(
172
142
            'Bazaar pack format 1 (introduced in 0.18)\n'
173
143
            'B3\n\nabc'
174
 
            'B3\n\nabc')
 
144
            'B3\n\nabc',
 
145
            output.getvalue())
175
146
 
176
147
    def test_add_bytes_record_invalid_name(self):
177
148
        """Adding a Bytes record with a name with whitespace in it raises
178
149
        InvalidRecordError.
179
150
        """
180
 
        self.writer.begin()
 
151
        output = StringIO()
 
152
        writer = pack.ContainerWriter(output.write)
 
153
        writer.begin()
181
154
        self.assertRaises(
182
155
            errors.InvalidRecordError,
183
 
            self.writer.add_bytes_record, 'abc', names=[('bad name', )])
 
156
            writer.add_bytes_record, 'abc', names=[('bad name', )])
184
157
 
185
158
    def test_add_bytes_records_add_to_records_written(self):
186
159
        """Adding a Bytes record increments the records_written counter."""
187
 
        self.writer.begin()
188
 
        self.writer.add_bytes_record('foo', names=[])
189
 
        self.assertEqual(1, self.writer.records_written)
190
 
        self.writer.add_bytes_record('foo', names=[])
191
 
        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)
192
167
 
193
168
 
194
169
class TestContainerReader(tests.TestCase):
195
 
    """Tests for the ContainerReader.
196
 
 
197
 
    The ContainerReader reads format 1 containers, so these tests explicitly
198
 
    test how it reacts to format 1 data.  If a new version of the format is
199
 
    added, then separate tests for that format should be added.
200
 
    """
201
170
 
202
171
    def get_reader_for(self, bytes):
203
172
        stream = StringIO(bytes)
206
175
 
207
176
    def test_construct(self):
208
177
        """Test constructing a ContainerReader.
209
 
 
 
178
        
210
179
        This uses None as the output stream to show that the constructor doesn't
211
180
        try to use the input stream.
212
181
        """
244
213
 
245
214
    def test_container_with_one_unnamed_record(self):
246
215
        """Read a container with one Bytes record.
247
 
 
 
216
        
248
217
        Parsing Bytes records is more thoroughly exercised by
249
218
        TestBytesRecordReader.  This test is here to ensure that
250
219
        ContainerReader's integration with BytesRecordReader is working.
327
296
        reader = self.get_reader_for(
328
297
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
329
298
        self.assertRaises(errors.InvalidRecordError, reader.validate)
330
 
 
 
299
        
331
300
 
332
301
class TestBytesRecordReader(tests.TestCase):
333
 
    """Tests for reading and validating Bytes records with
334
 
    BytesRecordReader.
335
 
 
336
 
    Like TestContainerReader, this explicitly tests the reading of format 1
337
 
    data.  If a new version of the format is added, then a separate set of
338
 
    tests for reading that format should be added.
339
 
    """
 
302
    """Tests for reading and validating Bytes records with BytesRecordReader."""
340
303
 
341
304
    def get_reader_for(self, bytes):
342
305
        stream = StringIO(bytes)
386
349
    def test_early_eof(self):
387
350
        """Tests for premature EOF occuring during parsing Bytes records with
388
351
        BytesRecordReader.
389
 
 
 
352
        
390
353
        A incomplete container might be interrupted at any point.  The
391
354
        BytesRecordReader needs to cope with the input stream running out no
392
355
        matter where it is in the parsing process.
519
482
    """Tests of the ReadVFile class.
520
483
 
521
484
    Error cases are deliberately undefined: this code adapts the underlying
522
 
    transport interface to a single 'streaming read' interface as
 
485
    transport interface to a single 'streaming read' interface as 
523
486
    ContainerReader needs.
524
487
    """
525
488
 
560
523
        results.append(f.readline())
561
524
        results.append(f.read(4))
562
525
        self.assertEqual(['0', '\n', '2\n4\n'], results)
563
 
 
564
 
 
565
 
class PushParserTestCase(tests.TestCase):
566
 
    """Base class for TestCases involving ContainerPushParser."""
567
 
 
568
 
    def make_parser_expecting_record_type(self):
569
 
        parser = pack.ContainerPushParser()
570
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\n")
571
 
        return parser
572
 
 
573
 
    def make_parser_expecting_bytes_record(self):
574
 
        parser = pack.ContainerPushParser()
575
 
        parser.accept_bytes("Bazaar pack format 1 (introduced in 0.18)\nB")
576
 
        return parser
577
 
 
578
 
    def assertRecordParsing(self, expected_record, bytes):
579
 
        """Assert that 'bytes' is parsed as a given bytes record.
580
 
 
581
 
        :param expected_record: A tuple of (names, bytes).
582
 
        """
583
 
        parser = self.make_parser_expecting_bytes_record()
584
 
        parser.accept_bytes(bytes)
585
 
        parsed_records = parser.read_pending_records()
586
 
        self.assertEqual([expected_record], parsed_records)
587
 
 
588
 
 
589
 
class TestContainerPushParser(PushParserTestCase):
590
 
    """Tests for ContainerPushParser.
591
 
 
592
 
    The ContainerPushParser reads format 1 containers, so these tests
593
 
    explicitly test how it reacts to format 1 data.  If a new version of the
594
 
    format is added, then separate tests for that format should be added.
595
 
    """
596
 
 
597
 
    def test_construct(self):
598
 
        """ContainerPushParser can be constructed."""
599
 
        pack.ContainerPushParser()
600
 
 
601
 
    def test_multiple_records_at_once(self):
602
 
        """If multiple records worth of data are fed to the parser in one
603
 
        string, the parser will correctly parse all the records.
604
 
 
605
 
        (A naive implementation might stop after parsing the first record.)
606
 
        """
607
 
        parser = self.make_parser_expecting_record_type()
608
 
        parser.accept_bytes("B5\nname1\n\nbody1B5\nname2\n\nbody2")
609
 
        self.assertEqual(
610
 
            [([('name1',)], 'body1'), ([('name2',)], 'body2')],
611
 
            parser.read_pending_records())
612
 
 
613
 
    def test_multiple_empty_records_at_once(self):
614
 
        """If multiple empty records worth of data are fed to the parser in one
615
 
        string, the parser will correctly parse all the records.
616
 
 
617
 
        (A naive implementation might stop after parsing the first empty
618
 
        record, because the buffer size had not changed.)
619
 
        """
620
 
        parser = self.make_parser_expecting_record_type()
621
 
        parser.accept_bytes("B0\nname1\n\nB0\nname2\n\n")
622
 
        self.assertEqual(
623
 
            [([('name1',)], ''), ([('name2',)], '')],
624
 
            parser.read_pending_records())
625
 
 
626
 
 
627
 
class TestContainerPushParserBytesParsing(PushParserTestCase):
628
 
    """Tests for reading Bytes records with ContainerPushParser.
629
 
 
630
 
    The ContainerPushParser reads format 1 containers, so these tests
631
 
    explicitly test how it reacts to format 1 data.  If a new version of the
632
 
    format is added, then separate tests for that format should be added.
633
 
    """
634
 
 
635
 
    def test_record_with_no_name(self):
636
 
        """Reading a Bytes record with no name returns an empty list of
637
 
        names.
638
 
        """
639
 
        self.assertRecordParsing(([], 'aaaaa'), "5\n\naaaaa")
640
 
 
641
 
    def test_record_with_one_name(self):
642
 
        """Reading a Bytes record with one name returns a list of just that
643
 
        name.
644
 
        """
645
 
        self.assertRecordParsing(
646
 
            ([('name1', )], 'aaaaa'),
647
 
            "5\nname1\n\naaaaa")
648
 
 
649
 
    def test_record_with_two_names(self):
650
 
        """Reading a Bytes record with two names returns a list of both names.
651
 
        """
652
 
        self.assertRecordParsing(
653
 
            ([('name1', ), ('name2', )], 'aaaaa'),
654
 
            "5\nname1\nname2\n\naaaaa")
655
 
 
656
 
    def test_record_with_two_part_names(self):
657
 
        """Reading a Bytes record with a two_part name reads both."""
658
 
        self.assertRecordParsing(
659
 
            ([('name1', 'name2')], 'aaaaa'),
660
 
            "5\nname1\x00name2\n\naaaaa")
661
 
 
662
 
    def test_invalid_length(self):
663
 
        """If the length-prefix is not a number, parsing raises
664
 
        InvalidRecordError.
665
 
        """
666
 
        parser = self.make_parser_expecting_bytes_record()
667
 
        self.assertRaises(
668
 
            errors.InvalidRecordError, parser.accept_bytes, "not a number\n")
669
 
 
670
 
    def test_incomplete_record(self):
671
 
        """If the bytes seen so far don't form a complete record, then there
672
 
        will be nothing returned by read_pending_records.
673
 
        """
674
 
        parser = self.make_parser_expecting_bytes_record()
675
 
        parser.accept_bytes("5\n\nabcd")
676
 
        self.assertEqual([], parser.read_pending_records())
677
 
 
678
 
    def test_accept_nothing(self):
679
 
        """The edge case of parsing an empty string causes no error."""
680
 
        parser = self.make_parser_expecting_bytes_record()
681
 
        parser.accept_bytes("")
682
 
 
683
 
    def assertInvalidRecord(self, bytes):
684
 
        """Assert that parsing the given bytes will raise an
685
 
        InvalidRecordError.
686
 
        """
687
 
        parser = self.make_parser_expecting_bytes_record()
688
 
        self.assertRaises(
689
 
            errors.InvalidRecordError, parser.accept_bytes, bytes)
690
 
 
691
 
    def test_read_invalid_name_whitespace(self):
692
 
        """Names must have no whitespace."""
693
 
        # A name with a space.
694
 
        self.assertInvalidRecord("0\nbad name\n\n")
695
 
 
696
 
        # A name with a tab.
697
 
        self.assertInvalidRecord("0\nbad\tname\n\n")
698
 
 
699
 
        # A name with a vertical tab.
700
 
        self.assertInvalidRecord("0\nbad\vname\n\n")
701
 
 
702
 
    def test_repeated_read_pending_records(self):
703
 
        """read_pending_records will not return the same record twice."""
704
 
        parser = self.make_parser_expecting_bytes_record()
705
 
        parser.accept_bytes("6\n\nabcdef")
706
 
        self.assertEqual([([], 'abcdef')], parser.read_pending_records())
707
 
        self.assertEqual([], parser.read_pending_records())
708
 
 
709