369
369
"""Ensure that iteration through ids works properly"""
370
370
btree = self.make_tree_1()[0]
371
371
self.assertEqual(self.sorted_ids(btree),
372
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
372
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
373
373
btree.note_deletion("grandparent/parent/file")
374
374
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
375
375
btree.note_last_changed("grandparent/alt_parent/fool",
376
376
"revisionidiguess")
377
377
self.assertEqual(self.sorted_ids(btree),
378
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
378
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
381
381
class BundleTester1(tests.TestCaseWithTransport):
606
606
for path, status, kind, fileid, entry in base_files:
607
607
# Check that the meta information is the same
608
608
self.assertEqual(base_tree.get_file_size(path, fileid),
609
to_tree.get_file_size(to_tree.id2path(fileid)))
609
to_tree.get_file_size(to_tree.id2path(fileid)))
610
610
self.assertEqual(base_tree.get_file_sha1(path, fileid),
611
to_tree.get_file_sha1(to_tree.id2path(fileid)))
611
to_tree.get_file_sha1(to_tree.id2path(fileid)))
612
612
# Check that the contents are the same
613
613
# This is pretty expensive
614
614
# self.assertEqual(base_tree.get_file(fileid).read(),
628
628
# Make sure we can handle files with spaces, tabs, other
629
629
# bogus characters
630
630
self.build_tree([
633
, 'b1/dir/filein subdir.c'
634
, 'b1/dir/WithCaps.txt'
635
, 'b1/dir/ pre space'
638
, 'b1/sub/sub/nonempty.txt'
631
'b1/with space.txt', 'b1/dir/', 'b1/dir/filein subdir.c', 'b1/dir/WithCaps.txt', 'b1/dir/ pre space', 'b1/sub/', 'b1/sub/sub/', 'b1/sub/sub/nonempty.txt'
640
633
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
641
634
('b1/dir/nolastnewline.txt', b'bloop')])
642
635
tt = TreeTransform(self.tree1)
646
639
# a (length-prefixed) record containing it later.
647
640
self.tree1.add('with space.txt', b'withspace-id')
650
, 'dir/filein subdir.c'
653
, 'dir/nolastnewline.txt'
656
, 'sub/sub/nonempty.txt'
657
, 'sub/sub/emptyfile.txt'
642
'dir', 'dir/filein subdir.c', 'dir/WithCaps.txt', 'dir/ pre space', 'dir/nolastnewline.txt', 'sub', 'sub/sub', 'sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt'
659
644
self.tree1.commit('add whitespace', rev_id=b'a@cset-0-2')
661
646
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
678
661
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
679
662
self.assertRaises((errors.TestamentMismatch,
680
errors.VersionedFileInvalidChecksum,
681
errors.BadBundle), self.get_invalid_bundle,
682
b'a@cset-0-2', b'a@cset-0-3')
663
errors.VersionedFileInvalidChecksum,
664
errors.BadBundle), self.get_invalid_bundle,
665
b'a@cset-0-2', b'a@cset-0-3')
683
666
# Check a rollup bundle
684
667
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
692
675
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-4')
695
with open('b1/sub/dir/WithCaps.txt', 'ab') as f: f.write(b'\nAdding some text\n')
696
with open('b1/sub/dir/ pre space', 'ab') as f: f.write(
697
b'\r\nAdding some\r\nDOS format lines\r\n')
698
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f: f.write(b'\n')
678
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
679
f.write(b'\nAdding some text\n')
680
with open('b1/sub/dir/ pre space', 'ab') as f:
682
b'\r\nAdding some\r\nDOS format lines\r\n')
683
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
699
685
self.tree1.rename_one('sub/dir/ pre space',
700
686
'sub/ start space')
701
687
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
735
721
if getattr(bundle, 'revision_tree', None) is not None:
736
722
# Not all bundle formats supports revision_tree
737
723
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
738
self.assertEqual(link_target, bund_tree.get_symlink_target(link_name))
725
link_target, bund_tree.get_symlink_target(link_name))
740
727
tt = TreeTransform(self.tree1)
741
728
trans_id = tt.trans_id_tree_path(link_name)
781
768
tt = TreeTransform(self.tree1)
784
tt.new_file('file', tt.root, [b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
771
tt.new_file('file', tt.root, [
772
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
785
773
tt.new_file('file2', tt.root, [b'\x01\n\x02\r\x03\n\x04\r\xff'],
788
776
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
789
777
self.get_valid_bundle(b'null:', b'b@cset-0-1')
850
838
self.tree1 = self.make_branch_and_tree('b1')
851
839
self.b1 = self.tree1.branch
853
with open('b1/one', 'wb') as f: f.write(b'one\n')
841
with open('b1/one', 'wb') as f:
854
843
self.tree1.add('one')
855
844
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
856
with open('b1/one', 'wb') as f: f.write(b'two\n')
845
with open('b1/one', 'wb') as f:
857
847
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
858
with open('b1/one', 'wb') as f: f.write(b'three\n')
848
with open('b1/one', 'wb') as f:
859
850
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
860
851
bundle_file = BytesIO()
861
852
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
940
930
bundle = self.get_valid_bundle(b'null:', b'white-1')
943
with open('b1/trailing space ', 'ab') as f: f.write(b'add some text\n')
933
with open('b1/trailing space ', 'ab') as f:
934
f.write(b'add some text\n')
944
935
self.tree1.commit('add text', rev_id=b'white-2')
946
937
bundle = self.get_valid_bundle(b'white-1', b'white-2')
1008
999
tree.lock_write()
1009
1000
self.addCleanup(tree.unlock)
1010
1001
tree.add([''], [b'TREE_ROOT'])
1011
tree.commit('One', revprops={u'one': 'two', u'empty': ''}, rev_id=b'rev1')
1002
tree.commit('One', revprops={u'one': 'two',
1003
u'empty': ''}, rev_id=b'rev1')
1012
1004
self.b1 = tree.branch
1013
1005
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1014
1006
bundle = read_bundle(bundle_sio)
1015
1007
revision_info = bundle.revisions[0]
1016
1008
self.assertEqual(b'rev1', revision_info.revision_id)
1017
1009
rev = revision_info.as_revision()
1018
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1010
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1019
1011
rev.properties)
1021
1013
def test_bundle_sorted_properties(self):
1027
1019
tree.add([''], [b'TREE_ROOT'])
1028
1020
tree.commit('One', rev_id=b'rev1',
1029
revprops={u'a':'4', u'b':'3', u'c':'2', u'd':'1'})
1021
revprops={u'a': '4', u'b': '3', u'c': '2', u'd': '1'})
1030
1022
self.b1 = tree.branch
1031
1023
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1032
1024
bundle = read_bundle(bundle_sio)
1033
1025
revision_info = bundle.revisions[0]
1034
1026
self.assertEqual(b'rev1', revision_info.revision_id)
1035
1027
rev = revision_info.as_revision()
1036
self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
1037
'd':'1'}, rev.properties)
1028
self.assertEqual({'branch-nick': 'tree', 'a': '4', 'b': '3', 'c': '2',
1029
'd': '1'}, rev.properties)
1039
1031
def test_bundle_unicode_properties(self):
1040
1032
"""We should be able to round trip a non-ascii property."""
1049
1041
# However, Testaments assert than they are str(), and thus should not
1051
1043
tree.commit('One', rev_id=b'rev1',
1052
revprops={u'omega':u'\u03a9', u'alpha':u'\u03b1'})
1044
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1053
1045
self.b1 = tree.branch
1054
1046
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1055
1047
bundle = read_bundle(bundle_sio)
1056
1048
revision_info = bundle.revisions[0]
1057
1049
self.assertEqual(b'rev1', revision_info.revision_id)
1058
1050
rev = revision_info.as_revision()
1059
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1060
'alpha':u'\u03b1'}, rev.properties)
1051
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1052
'alpha': u'\u03b1'}, rev.properties)
1062
1054
def test_bundle_with_ghosts(self):
1063
1055
tree = self.make_branch_and_tree('tree')
1104
1096
root_id = inv.root.file_id
1105
1097
repo.lock_read()
1106
1098
self.addCleanup(repo.unlock)
1107
self.assertEqual({(root_id, b'rev1'):(),
1108
(root_id, b'rev2'):((root_id, b'rev1'),)},
1109
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1099
self.assertEqual({(root_id, b'rev1'): (),
1100
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1101
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1111
1103
def test_inv_hash_across_serializers(self):
1112
1104
repo = self.make_repo_with_installed_revisions()
1190
1182
file_ids = set(
1191
1183
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1192
1184
if f is not None)
1193
self.assertEqual({(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1186
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1194
1187
bundle.install_revisions(target.branch.repository)
1204
1197
tree.lock_write()
1205
1198
self.addCleanup(tree.unlock)
1206
1199
tree.add([''], [b'TREE_ROOT'])
1207
tree.commit('One', revprops={u'one':'two', u'empty':''}, rev_id=b'rev1')
1200
tree.commit('One', revprops={u'one': 'two',
1201
u'empty': ''}, rev_id=b'rev1')
1208
1202
self.b1 = tree.branch
1209
1203
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1210
1204
self.assertContainsRe(bundle_sio.getvalue(),
1212
1206
b'# branch-nick: tree\n'
1214
1208
b'# one: two\n'
1216
1210
bundle = read_bundle(bundle_sio)
1217
1211
revision_info = bundle.revisions[0]
1218
1212
self.assertEqual(b'rev1', revision_info.revision_id)
1219
1213
rev = revision_info.as_revision()
1220
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1214
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1221
1215
rev.properties)
1223
1217
def get_bundle_tree(self, bundle, revision_id):
1236
1230
tree.lock_write()
1237
1231
self.addCleanup(tree.unlock)
1238
1232
tree.add([''], [b'TREE_ROOT'])
1239
tree.commit('One', revprops={u'one':'two', u'empty':''}, rev_id=b'rev1')
1233
tree.commit('One', revprops={u'one': 'two',
1234
u'empty': ''}, rev_id=b'rev1')
1240
1235
self.b1 = tree.branch
1241
1236
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1242
1237
txt = bundle_sio.getvalue()
1243
1238
loc = txt.find(b'# empty: ') + len(b'# empty:')
1244
1239
# Create a new bundle, which strips the trailing space after empty
1245
bundle_sio = BytesIO(txt[:loc] + txt[loc+1:])
1240
bundle_sio = BytesIO(txt[:loc] + txt[loc + 1:])
1247
1242
self.assertContainsRe(bundle_sio.getvalue(),
1248
1243
b'# properties:\n'
1249
1244
b'# branch-nick: tree\n'
1251
1246
b'# one: two\n'
1253
1248
bundle = read_bundle(bundle_sio)
1254
1249
revision_info = bundle.revisions[0]
1255
1250
self.assertEqual(b'rev1', revision_info.revision_id)
1256
1251
rev = revision_info.as_revision()
1257
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1252
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1258
1253
rev.properties)
1260
1255
def test_bundle_sorted_properties(self):
1266
1261
tree.add([''], [b'TREE_ROOT'])
1267
1262
tree.commit('One', rev_id=b'rev1',
1268
revprops={u'a':'4', u'b':'3', u'c':'2', u'd':'1'})
1263
revprops={u'a': '4', u'b': '3', u'c': '2', u'd': '1'})
1269
1264
self.b1 = tree.branch
1270
1265
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1271
1266
self.assertContainsRe(bundle_sio.getvalue(),
1275
1270
b'# branch-nick: tree\n'
1279
1274
bundle = read_bundle(bundle_sio)
1280
1275
revision_info = bundle.revisions[0]
1281
1276
self.assertEqual(b'rev1', revision_info.revision_id)
1282
1277
rev = revision_info.as_revision()
1283
self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
1284
'd':'1'}, rev.properties)
1278
self.assertEqual({'branch-nick': 'tree', 'a': '4', 'b': '3', 'c': '2',
1279
'd': '1'}, rev.properties)
1286
1281
def test_bundle_unicode_properties(self):
1287
1282
"""We should be able to round trip a non-ascii property."""
1296
1291
# However, Testaments assert than they are str(), and thus should not
1298
1293
tree.commit('One', rev_id=b'rev1',
1299
revprops={u'omega':u'\u03a9', u'alpha':u'\u03b1'})
1294
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1300
1295
self.b1 = tree.branch
1301
1296
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1302
1297
self.assertContainsRe(bundle_sio.getvalue(),
1304
1299
b'# alpha: \xce\xb1\n'
1305
1300
b'# branch-nick: tree\n'
1306
1301
b'# omega: \xce\xa9\n'
1308
1303
bundle = read_bundle(bundle_sio)
1309
1304
revision_info = bundle.revisions[0]
1310
1305
self.assertEqual(b'rev1', revision_info.revision_id)
1311
1306
rev = revision_info.as_revision()
1312
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1313
'alpha':u'\u03b1'}, rev.properties)
1307
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1308
'alpha': u'\u03b1'}, rev.properties)
1316
1311
class V09BundleKnit2Tester(V08BundleTester):
1427
1423
# Turn the 'iterators_of_bytes' back into simple strings for comparison
1428
1424
repo_texts = dict((i, b''.join(content)) for i, content
1429
1425
in target_repo.iter_files_bytes(
1430
[(b'fileid-2', b'rev1', '1'),
1431
(b'fileid-2', b'rev2', '2')]))
1432
self.assertEqual({'1':b'contents1\nstatic\n',
1433
'2':b'contents2\nstatic\n'},
1426
[(b'fileid-2', b'rev1', '1'),
1427
(b'fileid-2', b'rev2', '2')]))
1428
self.assertEqual({'1': b'contents1\nstatic\n',
1429
'2': b'contents2\nstatic\n'},
1435
1431
rtree = target_repo.revision_tree(b'rev2')
1436
1432
inventory_vf = target_repo.inventories
1478
1475
serializer = BundleSerializerV4('4')
1479
1476
with tree_a.lock_read():
1480
serializer.write_bundle(tree_a.branch.repository, b'B', b'null:', s)
1477
serializer.write_bundle(
1478
tree_a.branch.repository, b'B', b'null:', s)
1482
1480
install_bundle(repo_b, serializer.read(s))
1483
1481
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1589
1587
self.assertEqual({b'parents': [],
1590
1588
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1591
1589
b'storage_kind': b'mpdiff',
1593
1591
# We should have an mpdiff that takes some lines from both parents.
1594
1592
self.assertEqualDiff(
1596
1594
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1597
1595
b'<directory file_id="root-id" name=""'
1598
b' revision="a@cset-0-1" />\n'
1596
b' revision="a@cset-0-1" />\n'
1599
1597
b'<file file_id="file-id" name="file" parent_id="root-id"'
1600
b' revision="a@cset-0-1"'
1601
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1602
b' text_size="17" />\n'
1598
b' revision="a@cset-0-1"'
1599
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1600
b' text_size="17" />\n'
1603
1601
b'</inventory>\n'
1606
1604
def test_multiple_inventories_as_xml(self):
1607
1605
self.make_merged_branch()
1608
1606
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1609
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1607
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1610
1608
reader = v4.BundleReader(sio, stream_input=False)
1611
1609
records = list(reader.iter_records())
1612
1610
self.assertEqual(3, len(records))
1617
1615
self.assertEqual({b'parents': [b'a@cset-0-1'],
1618
1616
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1619
1617
b'storage_kind': b'mpdiff',
1621
1619
metadata_2b = records[1][1]
1622
1620
self.assertEqual({b'parents': [b'a@cset-0-1'],
1623
1621
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1624
1622
b'storage_kind': b'mpdiff',
1626
1624
metadata_3 = records[2][1]
1627
1625
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1628
1626
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1629
1627
b'storage_kind': b'mpdiff',
1631
1629
bytes_2a = records[0][0]
1632
1630
self.assertEqualDiff(
1665
1663
def test_creating_bundle_preserves_chk_pages(self):
1666
1664
self.make_merged_branch()
1667
1665
target = self.b1.controldir.sprout('target',
1668
revision_id=b'a@cset-0-2a').open_branch()
1666
revision_id=b'a@cset-0-2a').open_branch()
1669
1667
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1671
1669
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1705
1703
def check_valid(self, bundle):
1706
1704
"""Check that after whatever munging, the final object is valid."""
1707
1705
self.assertEqual([b'a@cset-0-2'],
1708
[r.revision_id for r in bundle.real_revisions])
1706
[r.revision_id for r in bundle.real_revisions])
1710
1708
def test_extra_whitespace(self):
1711
1709
bundle_txt = self.build_test_bundle()
1786
1784
writer.add_info_record({b'foo': b'bar'})
1787
1785
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1788
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1786
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1790
1788
fileobj.seek(0)
1791
1789
reader = v4.BundleReader(fileobj, stream_input=True)
1792
1790
record_iter = reader.iter_records()
1793
1791
record = next(record_iter)
1794
1792
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1795
'info', None, None), record)
1793
'info', None, None), record)
1796
1794
record = next(record_iter)
1797
1795
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1798
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1796
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1801
1799
def test_roundtrip_record_memory_hungry(self):
1802
1800
fileobj = BytesIO()
1805
1803
writer.add_info_record({b'foo': b'bar'})
1806
1804
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1807
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1805
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1809
1807
fileobj.seek(0)
1810
1808
reader = v4.BundleReader(fileobj, stream_input=False)
1811
1809
record_iter = reader.iter_records()
1812
1810
record = next(record_iter)
1813
1811
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1814
'info', None, None), record)
1812
'info', None, None), record)
1815
1813
record = next(record_iter)
1816
1814
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1817
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1815
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1820
1818
def test_encode_name(self):
1821
1819
self.assertEqual(b'revision/rev1',
1822
v4.BundleWriter.encode_name('revision', b'rev1'))
1820
v4.BundleWriter.encode_name('revision', b'rev1'))
1823
1821
self.assertEqual(b'file/rev//1/file-id-1',
1824
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1822
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1825
1823
self.assertEqual(b'info',
1826
v4.BundleWriter.encode_name('info', None, None))
1824
v4.BundleWriter.encode_name('info', None, None))
1828
1826
def test_decode_name(self):
1829
1827
self.assertEqual(('revision', b'rev1', None),
1830
v4.BundleReader.decode_name(b'revision/rev1'))
1828
v4.BundleReader.decode_name(b'revision/rev1'))
1831
1829
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1832
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1830
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1833
1831
self.assertEqual(('info', None, None),
1834
1832
v4.BundleReader.decode_name(b'info'))
1844
1842
record_iter = v4.BundleReader(fileobj).iter_records()
1845
1843
record = next(record_iter)
1846
1844
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1847
'info', None, None), record)
1845
'info', None, None), record)
1848
1846
self.assertRaises(errors.BadBundle, next, record_iter)