22
22
from bzrlib import pack, errors, tests
25
class TestContainerSerialiser(tests.TestCase):
26
"""Tests for the ContainerSerialiser class."""
28
def test_construct(self):
29
"""Test constructing a ContainerSerialiser."""
30
pack.ContainerSerialiser()
33
serialiser = pack.ContainerSerialiser()
34
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
38
serialiser = pack.ContainerSerialiser()
39
self.assertEqual('E', serialiser.end())
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)
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)
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)
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)
61
def test_bytes_record_whitespace_in_name_part(self):
62
serialiser = pack.ContainerSerialiser()
64
errors.InvalidRecordError,
65
serialiser.bytes_record, 'bytes', [('bad name',)])
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)
25
73
class TestContainerWriter(tests.TestCase):
76
super(TestContainerWriter, self).setUp()
77
self.output = StringIO()
78
self.writer = pack.ContainerWriter(self.output.write)
80
def assertOutput(self, expected_output):
81
"""Assert that the output of self.writer ContainerWriter is equal to
84
self.assertEqual(expected_output, self.output.getvalue())
27
86
def test_construct(self):
28
87
"""Test constructing a ContainerWriter.
30
This uses None as the output stream to show that the constructor doesn't
31
try to use the output stream.
89
This uses None as the output stream to show that the constructor
90
doesn't try to use the output stream.
33
92
writer = pack.ContainerWriter(None)
35
94
def test_begin(self):
36
95
"""The begin() method writes the container format marker line."""
38
writer = pack.ContainerWriter(output.write)
40
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\n',
97
self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\n')
43
99
def test_zero_records_written_after_begin(self):
44
100
"""After begin is written, 0 records have been written."""
46
writer = pack.ContainerWriter(output.write)
48
self.assertEqual(0, writer.records_written)
102
self.assertEqual(0, self.writer.records_written)
50
104
def test_end(self):
51
105
"""The end() method writes an End Marker record."""
53
writer = pack.ContainerWriter(output.write)
56
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
108
self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
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."""
62
writer = pack.ContainerWriter(output.write)
65
self.assertEqual(0, writer.records_written)
114
self.assertEqual(0, self.writer.records_written)
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."""
70
writer = pack.ContainerWriter(output.write)
72
writer.add_bytes_record('foo', names=[])
74
self.assertEqual(1, writer.records_written)
119
self.writer.add_bytes_record('foo', names=[])
121
self.assertEqual(1, self.writer.records_written)
76
123
def test_add_bytes_record_no_name(self):
77
124
"""Add a bytes record with no name."""
79
writer = pack.ContainerWriter(output.write)
81
offset, length = writer.add_bytes_record('abc', names=[])
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',
129
'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
86
131
def test_add_bytes_record_one_name(self):
87
132
"""Add a bytes record with one name."""
89
writer = pack.ContainerWriter(output.write)
91
offset, length = writer.add_bytes_record('abc', names=[('name1', )])
135
offset, length = self.writer.add_bytes_record(
136
'abc', names=[('name1', )])
92
137
self.assertEqual((42, 13), (offset, length))
94
'Bazaar pack format 1 (introduced in 0.18)\n'
98
def test_add_bytes_record_two_names(self):
99
"""Add a bytes record with two names."""
101
writer = pack.ContainerWriter(output.write)
103
offset, length = writer.add_bytes_record('abc', names=[('name1', ), ('name2', )])
104
self.assertEqual((42, 19), (offset, length))
106
'Bazaar pack format 1 (introduced in 0.18)\n'
107
'B3\nname1\nname2\n\nabc',
110
def test_add_bytes_record_two_names(self):
111
"""Add a bytes record with two names."""
113
writer = pack.ContainerWriter(output.write)
115
offset, length = writer.add_bytes_record('abc', names=[('name1', ), ('name2', )])
116
self.assertEqual((42, 19), (offset, length))
118
'Bazaar pack format 1 (introduced in 0.18)\n'
119
'B3\nname1\nname2\n\nabc',
139
'Bazaar pack format 1 (introduced in 0.18)\n'
142
def test_add_bytes_record_split_writes(self):
143
"""Write a large record which does multiple IOs"""
146
real_write = self.writer.write_func
148
def record_writes(bytes):
150
return real_write(bytes)
152
self.writer.write_func = record_writes
153
self.writer._JOIN_WRITES_THRESHOLD = 2
156
offset, length = self.writer.add_bytes_record(
157
'abcabc', names=[('name1', )])
158
self.assertEqual((42, 16), (offset, length))
160
'Bazaar pack format 1 (introduced in 0.18)\n'
161
'B6\nname1\n\nabcabc')
164
'Bazaar pack format 1 (introduced in 0.18)\n',
169
def test_add_bytes_record_two_names(self):
170
"""Add a bytes record with two names."""
172
offset, length = self.writer.add_bytes_record(
173
'abc', names=[('name1', ), ('name2', )])
174
self.assertEqual((42, 19), (offset, length))
176
'Bazaar pack format 1 (introduced in 0.18)\n'
177
'B3\nname1\nname2\n\nabc')
179
def test_add_bytes_record_two_names(self):
180
"""Add a bytes record with two names."""
182
offset, length = self.writer.add_bytes_record(
183
'abc', names=[('name1', ), ('name2', )])
184
self.assertEqual((42, 19), (offset, length))
186
'Bazaar pack format 1 (introduced in 0.18)\n'
187
'B3\nname1\nname2\n\nabc')
122
189
def test_add_bytes_record_two_element_name(self):
123
190
"""Add a bytes record with a two-element name."""
125
writer = pack.ContainerWriter(output.write)
127
offset, length = writer.add_bytes_record('abc', names=[('name1', 'name2')])
192
offset, length = self.writer.add_bytes_record(
193
'abc', names=[('name1', 'name2')])
128
194
self.assertEqual((42, 19), (offset, length))
130
196
'Bazaar pack format 1 (introduced in 0.18)\n'
131
'B3\nname1\x00name2\n\nabc',
197
'B3\nname1\x00name2\n\nabc')
134
199
def test_add_second_bytes_record_gets_higher_offset(self):
136
writer = pack.ContainerWriter(output.write)
138
writer.add_bytes_record('abc', names=[])
139
offset, length = writer.add_bytes_record('abc', names=[])
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))
142
205
'Bazaar pack format 1 (introduced in 0.18)\n'
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.
152
writer = pack.ContainerWriter(output.write)
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', )])
158
218
def test_add_bytes_records_add_to_records_written(self):
159
219
"""Adding a Bytes record increments the records_written counter."""
161
writer = pack.ContainerWriter(output.write)
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)
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)
169
227
class TestContainerReader(tests.TestCase):
228
"""Tests for the ContainerReader.
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.
171
235
def get_reader_for(self, bytes):
172
236
stream = StringIO(bytes)
523
594
results.append(f.readline())
524
595
results.append(f.read(4))
525
596
self.assertEqual(['0', '\n', '2\n4\n'], results)
599
class PushParserTestCase(tests.TestCase):
600
"""Base class for TestCases involving ContainerPushParser."""
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")
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")
612
def assertRecordParsing(self, expected_record, bytes):
613
"""Assert that 'bytes' is parsed as a given bytes record.
615
:param expected_record: A tuple of (names, bytes).
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)
623
class TestContainerPushParser(PushParserTestCase):
624
"""Tests for ContainerPushParser.
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.
631
def test_construct(self):
632
"""ContainerPushParser can be constructed."""
633
pack.ContainerPushParser()
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.
639
(A naive implementation might stop after parsing the first record.)
641
parser = self.make_parser_expecting_record_type()
642
parser.accept_bytes("B5\nname1\n\nbody1B5\nname2\n\nbody2")
644
[([('name1',)], 'body1'), ([('name2',)], 'body2')],
645
parser.read_pending_records())
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.
651
(A naive implementation might stop after parsing the first empty
652
record, because the buffer size had not changed.)
654
parser = self.make_parser_expecting_record_type()
655
parser.accept_bytes("B0\nname1\n\nB0\nname2\n\n")
657
[([('name1',)], ''), ([('name2',)], '')],
658
parser.read_pending_records())
661
class TestContainerPushParserBytesParsing(PushParserTestCase):
662
"""Tests for reading Bytes records with ContainerPushParser.
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.
669
def test_record_with_no_name(self):
670
"""Reading a Bytes record with no name returns an empty list of
673
self.assertRecordParsing(([], 'aaaaa'), "5\n\naaaaa")
675
def test_record_with_one_name(self):
676
"""Reading a Bytes record with one name returns a list of just that
679
self.assertRecordParsing(
680
([('name1', )], 'aaaaa'),
683
def test_record_with_two_names(self):
684
"""Reading a Bytes record with two names returns a list of both names.
686
self.assertRecordParsing(
687
([('name1', ), ('name2', )], 'aaaaa'),
688
"5\nname1\nname2\n\naaaaa")
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")
696
def test_invalid_length(self):
697
"""If the length-prefix is not a number, parsing raises
700
parser = self.make_parser_expecting_bytes_record()
702
errors.InvalidRecordError, parser.accept_bytes, "not a number\n")
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.
708
parser = self.make_parser_expecting_bytes_record()
709
parser.accept_bytes("5\n\nabcd")
710
self.assertEqual([], parser.read_pending_records())
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("")
717
def assertInvalidRecord(self, bytes):
718
"""Assert that parsing the given bytes will raise an
721
parser = self.make_parser_expecting_bytes_record()
723
errors.InvalidRecordError, parser.accept_bytes, bytes)
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")
731
self.assertInvalidRecord("0\nbad\tname\n\n")
733
# A name with a vertical tab.
734
self.assertInvalidRecord("0\nbad\vname\n\n")
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())