~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_pack.py

  • Committer: Ian Clatworthy
  • Date: 2009-09-09 11:43:10 UTC
  • mto: (4634.37.2 prepare-2.0)
  • mto: This revision was merged to the branch mainline in revision 4689.
  • Revision ID: ian.clatworthy@canonical.com-20090909114310-glw7tv76i5gnx9pt
put rules back in Makefile supporting plain-style docs

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
 
25
68
class TestContainerWriter(tests.TestCase):
26
69
 
 
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
 
27
81
    def test_construct(self):
28
82
        """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.
 
83
 
 
84
        This uses None as the output stream to show that the constructor
 
85
        doesn't try to use the output stream.
32
86
        """
33
87
        writer = pack.ContainerWriter(None)
34
88
 
35
89
    def test_begin(self):
36
90
        """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())
 
91
        self.writer.begin()
 
92
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\n')
42
93
 
43
94
    def test_zero_records_written_after_begin(self):
44
95
        """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)
 
96
        self.writer.begin()
 
97
        self.assertEqual(0, self.writer.records_written)
49
98
 
50
99
    def test_end(self):
51
100
        """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())
 
101
        self.writer.begin()
 
102
        self.writer.end()
 
103
        self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
58
104
 
59
105
    def test_empty_end_does_not_add_a_record_to_records_written(self):
60
106
        """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)
 
107
        self.writer.begin()
 
108
        self.writer.end()
 
109
        self.assertEqual(0, self.writer.records_written)
66
110
 
67
111
    def test_non_empty_end_does_not_add_a_record_to_records_written(self):
68
112
        """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)
 
113
        self.writer.begin()
 
114
        self.writer.add_bytes_record('foo', names=[])
 
115
        self.writer.end()
 
116
        self.assertEqual(1, self.writer.records_written)
75
117
 
76
118
    def test_add_bytes_record_no_name(self):
77
119
        """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=[])
 
120
        self.writer.begin()
 
121
        offset, length = self.writer.add_bytes_record('abc', names=[])
82
122
        self.assertEqual((42, 7), (offset, length))
83
 
        self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
84
 
                         output.getvalue())
 
123
        self.assertOutput(
 
124
            'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
85
125
 
86
126
    def test_add_bytes_record_one_name(self):
87
127
        """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', )])
 
128
        self.writer.begin()
 
129
        offset, length = self.writer.add_bytes_record(
 
130
            'abc', names=[('name1', )])
92
131
        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())
 
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')
121
155
 
122
156
    def test_add_bytes_record_two_element_name(self):
123
157
        """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')])
 
158
        self.writer.begin()
 
159
        offset, length = self.writer.add_bytes_record(
 
160
            'abc', names=[('name1', 'name2')])
128
161
        self.assertEqual((42, 19), (offset, length))
129
 
        self.assertEqual(
 
162
        self.assertOutput(
130
163
            'Bazaar pack format 1 (introduced in 0.18)\n'
131
 
            'B3\nname1\x00name2\n\nabc',
132
 
            output.getvalue())
 
164
            'B3\nname1\x00name2\n\nabc')
133
165
 
134
166
    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=[])
 
167
        self.writer.begin()
 
168
        self.writer.add_bytes_record('abc', names=[])
 
169
        offset, length = self.writer.add_bytes_record('abc', names=[])
140
170
        self.assertEqual((49, 7), (offset, length))
141
 
        self.assertEqual(
 
171
        self.assertOutput(
142
172
            'Bazaar pack format 1 (introduced in 0.18)\n'
143
173
            'B3\n\nabc'
144
 
            'B3\n\nabc',
145
 
            output.getvalue())
 
174
            'B3\n\nabc')
146
175
 
147
176
    def test_add_bytes_record_invalid_name(self):
148
177
        """Adding a Bytes record with a name with whitespace in it raises
149
178
        InvalidRecordError.
150
179
        """
151
 
        output = StringIO()
152
 
        writer = pack.ContainerWriter(output.write)
153
 
        writer.begin()
 
180
        self.writer.begin()
154
181
        self.assertRaises(
155
182
            errors.InvalidRecordError,
156
 
            writer.add_bytes_record, 'abc', names=[('bad name', )])
 
183
            self.writer.add_bytes_record, 'abc', names=[('bad name', )])
157
184
 
158
185
    def test_add_bytes_records_add_to_records_written(self):
159
186
        """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)
 
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)
167
192
 
168
193
 
169
194
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
    """
170
201
 
171
202
    def get_reader_for(self, bytes):
172
203
        stream = StringIO(bytes)
175
206
 
176
207
    def test_construct(self):
177
208
        """Test constructing a ContainerReader.
178
 
        
 
209
 
179
210
        This uses None as the output stream to show that the constructor doesn't
180
211
        try to use the input stream.
181
212
        """
213
244
 
214
245
    def test_container_with_one_unnamed_record(self):
215
246
        """Read a container with one Bytes record.
216
 
        
 
247
 
217
248
        Parsing Bytes records is more thoroughly exercised by
218
249
        TestBytesRecordReader.  This test is here to ensure that
219
250
        ContainerReader's integration with BytesRecordReader is working.
296
327
        reader = self.get_reader_for(
297
328
            "Bazaar pack format 1 (introduced in 0.18)\nB0\n\xcc\n\nE")
298
329
        self.assertRaises(errors.InvalidRecordError, reader.validate)
299
 
        
 
330
 
300
331
 
301
332
class TestBytesRecordReader(tests.TestCase):
302
 
    """Tests for reading and validating Bytes records with BytesRecordReader."""
 
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
    """
303
340
 
304
341
    def get_reader_for(self, bytes):
305
342
        stream = StringIO(bytes)
349
386
    def test_early_eof(self):
350
387
        """Tests for premature EOF occuring during parsing Bytes records with
351
388
        BytesRecordReader.
352
 
        
 
389
 
353
390
        A incomplete container might be interrupted at any point.  The
354
391
        BytesRecordReader needs to cope with the input stream running out no
355
392
        matter where it is in the parsing process.
482
519
    """Tests of the ReadVFile class.
483
520
 
484
521
    Error cases are deliberately undefined: this code adapts the underlying
485
 
    transport interface to a single 'streaming read' interface as 
 
522
    transport interface to a single 'streaming read' interface as
486
523
    ContainerReader needs.
487
524
    """
488
525
 
523
560
        results.append(f.readline())
524
561
        results.append(f.read(4))
525
562
        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