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')
99
def test_zero_records_written_after_begin(self):
100
"""After begin is written, 0 records have been written."""
102
self.assertEqual(0, self.writer.records_written)
43
104
def test_end(self):
44
105
"""The end() method writes an End Marker record."""
46
writer = pack.ContainerWriter(output.write)
49
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nE',
108
self.assertOutput('Bazaar pack format 1 (introduced in 0.18)\nE')
110
def test_empty_end_does_not_add_a_record_to_records_written(self):
111
"""The end() method does not count towards the records written."""
114
self.assertEqual(0, self.writer.records_written)
116
def test_non_empty_end_does_not_add_a_record_to_records_written(self):
117
"""The end() method does not count towards the records written."""
119
self.writer.add_bytes_record('foo', names=[])
121
self.assertEqual(1, self.writer.records_written)
52
123
def test_add_bytes_record_no_name(self):
53
124
"""Add a bytes record with no name."""
55
writer = pack.ContainerWriter(output.write)
57
writer.add_bytes_record('abc', names=[])
58
self.assertEqual('Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc',
126
offset, length = self.writer.add_bytes_record('abc', names=[])
127
self.assertEqual((42, 7), (offset, length))
129
'Bazaar pack format 1 (introduced in 0.18)\nB3\n\nabc')
61
131
def test_add_bytes_record_one_name(self):
62
132
"""Add a bytes record with one name."""
64
writer = pack.ContainerWriter(output.write)
66
writer.add_bytes_record('abc', names=['name1'])
68
'Bazaar pack format 1 (introduced in 0.18)\n'
72
def test_add_bytes_record_two_names(self):
73
"""Add a bytes record with two names."""
75
writer = pack.ContainerWriter(output.write)
77
writer.add_bytes_record('abc', names=['name1', 'name2'])
79
'Bazaar pack format 1 (introduced in 0.18)\n'
80
'B3\nname1\nname2\n\nabc',
135
offset, length = self.writer.add_bytes_record(
136
'abc', names=[('name1', )])
137
self.assertEqual((42, 13), (offset, length))
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')
189
def test_add_bytes_record_two_element_name(self):
190
"""Add a bytes record with a two-element name."""
192
offset, length = self.writer.add_bytes_record(
193
'abc', names=[('name1', 'name2')])
194
self.assertEqual((42, 19), (offset, length))
196
'Bazaar pack format 1 (introduced in 0.18)\n'
197
'B3\nname1\x00name2\n\nabc')
199
def test_add_second_bytes_record_gets_higher_offset(self):
201
self.writer.add_bytes_record('abc', names=[])
202
offset, length = self.writer.add_bytes_record('abc', names=[])
203
self.assertEqual((49, 7), (offset, length))
205
'Bazaar pack format 1 (introduced in 0.18)\n'
83
209
def test_add_bytes_record_invalid_name(self):
84
210
"""Adding a Bytes record with a name with whitespace in it raises
85
211
InvalidRecordError.
88
writer = pack.ContainerWriter(output.write)
90
214
self.assertRaises(
91
215
errors.InvalidRecordError,
92
writer.add_bytes_record, 'abc', names=['bad name'])
216
self.writer.add_bytes_record, 'abc', names=[('bad name', )])
218
def test_add_bytes_records_add_to_records_written(self):
219
"""Adding a Bytes record increments the records_written counter."""
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)
95
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.
97
235
def get_reader_for(self, bytes):
98
236
stream = StringIO(bytes)
375
527
self.assertEqual('', get_bytes(99))
530
class TestMakeReadvReader(tests.TestCaseWithTransport):
532
def test_read_skipping_records(self):
533
pack_data = StringIO()
534
writer = pack.ContainerWriter(pack_data.write)
537
memos.append(writer.add_bytes_record('abc', names=[]))
538
memos.append(writer.add_bytes_record('def', names=[('name1', )]))
539
memos.append(writer.add_bytes_record('ghi', names=[('name2', )]))
540
memos.append(writer.add_bytes_record('jkl', names=[]))
542
transport = self.get_transport()
543
transport.put_bytes('mypack', pack_data.getvalue())
544
requested_records = [memos[0], memos[2]]
545
reader = pack.make_readv_reader(transport, 'mypack', requested_records)
547
for names, reader_func in reader.iter_records():
548
result.append((names, reader_func(None)))
549
self.assertEqual([([], 'abc'), ([('name2', )], 'ghi')], result)
552
class TestReadvFile(tests.TestCaseWithTransport):
553
"""Tests of the ReadVFile class.
555
Error cases are deliberately undefined: this code adapts the underlying
556
transport interface to a single 'streaming read' interface as
557
ContainerReader needs.
560
def test_read_bytes(self):
561
"""Test reading of both single bytes and all bytes in a hunk."""
562
transport = self.get_transport()
563
transport.put_bytes('sample', '0123456789')
564
f = pack.ReadVFile(transport.readv('sample', [(0,1), (1,2), (4,1), (6,2)]))
566
results.append(f.read(1))
567
results.append(f.read(2))
568
results.append(f.read(1))
569
results.append(f.read(1))
570
results.append(f.read(1))
571
self.assertEqual(['0', '12', '4', '6', '7'], results)
573
def test_readline(self):
574
"""Test using readline() as ContainerReader does.
576
This is always within a readv hunk, never across it.
578
transport = self.get_transport()
579
transport.put_bytes('sample', '0\n2\n4\n')
580
f = pack.ReadVFile(transport.readv('sample', [(0,2), (2,4)]))
582
results.append(f.readline())
583
results.append(f.readline())
584
results.append(f.readline())
585
self.assertEqual(['0\n', '2\n', '4\n'], results)
587
def test_readline_and_read(self):
588
"""Test exercising one byte reads, readline, and then read again."""
589
transport = self.get_transport()
590
transport.put_bytes('sample', '0\n2\n4\n')
591
f = pack.ReadVFile(transport.readv('sample', [(0,6)]))
593
results.append(f.read(1))
594
results.append(f.readline())
595
results.append(f.read(4))
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())