14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from cStringIO import StringIO
18
from io import BytesIO
31
27
revision as _mod_revision,
35
from bzrlib.bundle import read_mergeable_from_url
36
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
37
from bzrlib.bundle.bundle_data import BundleTree
38
from bzrlib.bzrdir import BzrDir
39
from bzrlib.directory_service import directories
40
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
41
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
42
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
43
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
44
from bzrlib.branch import Branch
45
from bzrlib.repofmt import knitrepo
46
from bzrlib.tests import (
35
from ..bundle.apply_bundle import install_bundle, merge_bundle
36
from ..bundle.bundle_data import BundleTree
37
from ..bundle.serializer import write_bundle, read_bundle, v09, v4
38
from ..bundle.serializer.v08 import BundleSerializerV08
39
from ..bundle.serializer.v09 import BundleSerializerV09
40
from ..bundle.serializer.v4 import BundleSerializerV4
41
from ..bzr import knitrepo
50
from bzrlib.transform import TreeTransform
46
from ..transform import TreeTransform
53
49
def get_text(vf, key):
54
50
"""Get the fulltext for a given revision id that is present in the vf"""
55
51
stream = vf.get_record_stream([key], 'unordered', True)
56
record = stream.next()
57
53
return record.get_bytes_as('fulltext')
60
56
def get_inventory_text(repo, revision_id):
61
57
"""Get the fulltext for the inventory at revision id"""
58
with repo.lock_read():
64
59
return get_text(repo.inventories, (revision_id,))
69
62
class MockTree(object):
70
64
def __init__(self):
71
from bzrlib.inventory import InventoryDirectory, ROOT_ID
65
from ..bzr.inventory import InventoryDirectory, ROOT_ID
72
66
object.__init__(self)
73
67
self.paths = {ROOT_ID: ""}
74
68
self.ids = {"": ROOT_ID}
76
70
self.root = InventoryDirectory(ROOT_ID, '', None)
78
inventory = property(lambda x:x)
81
return self.paths.iterkeys()
72
inventory = property(lambda x: x)
73
root_inventory = property(lambda x: x)
75
def get_root_id(self):
76
return self.root.file_id
78
def all_file_ids(self):
79
return set(self.paths.keys())
81
def all_versioned_paths(self):
82
return set(self.paths.values())
84
def is_executable(self, path):
85
# Not all the files are executable.
83
88
def __getitem__(self, file_id):
84
89
if file_id == self.root.file_id:
93
101
return self.ids[parent_dir]
95
103
def iter_entries(self):
96
for path, file_id in self.ids.iteritems():
104
for path, file_id in self.ids.items():
97
105
yield path, self[file_id]
99
def get_file_kind(self, file_id):
100
if file_id in self.contents:
107
def kind(self, path):
108
if path in self.contents:
103
111
kind = 'directory'
106
114
def make_entry(self, file_id, path):
107
from bzrlib.inventory import (InventoryEntry, InventoryFile
108
, InventoryDirectory, InventoryLink)
115
from ..bzr.inventory import (InventoryFile, InventoryDirectory,
117
if not isinstance(file_id, bytes):
118
raise TypeError(file_id)
109
119
name = os.path.basename(path)
110
kind = self.get_file_kind(file_id)
120
kind = self.kind(path)
111
121
parent_id = self.parent_id(file_id)
112
text_sha_1, text_size = self.contents_stats(file_id)
122
text_sha_1, text_size = self.contents_stats(path)
113
123
if kind == 'directory':
114
124
ie = InventoryDirectory(file_id, name, parent_id)
115
125
elif kind == 'file':
116
126
ie = InventoryFile(file_id, name, parent_id)
127
ie.text_sha1 = text_sha_1
128
ie.text_size = text_size
117
129
elif kind == 'symlink':
118
130
ie = InventoryLink(file_id, name, parent_id)
120
132
raise errors.BzrError('unknown kind %r' % kind)
121
ie.text_sha1 = text_sha_1
122
ie.text_size = text_size
125
135
def add_dir(self, file_id, path):
136
if not isinstance(file_id, bytes):
137
raise TypeError(file_id)
126
138
self.paths[file_id] = path
127
139
self.ids[path] = file_id
129
141
def add_file(self, file_id, path, contents):
142
if not isinstance(file_id, bytes):
143
raise TypeError(file_id)
130
144
self.add_dir(file_id, path)
131
self.contents[file_id] = contents
145
self.contents[path] = contents
133
147
def path2id(self, path):
134
148
return self.ids.get(path)
172
198
self.assertEqual(btree.old_path("grandparent/parent/file"),
173
199
"grandparent/parent/file")
175
self.assertEqual(btree.id2path("a"), "grandparent")
176
self.assertEqual(btree.id2path("b"), "grandparent/parent")
177
self.assertEqual(btree.id2path("c"), "grandparent/parent/file")
179
self.assertEqual(btree.path2id("grandparent"), "a")
180
self.assertEqual(btree.path2id("grandparent/parent"), "b")
181
self.assertEqual(btree.path2id("grandparent/parent/file"), "c")
183
self.assertTrue(btree.path2id("grandparent2") is None)
184
self.assertTrue(btree.path2id("grandparent2/parent") is None)
185
self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
201
self.assertEqual(btree.id2path(b"a"), "grandparent")
202
self.assertEqual(btree.id2path(b"b"), "grandparent/parent")
203
self.assertEqual(btree.id2path(b"c"), "grandparent/parent/file")
205
self.assertEqual(btree.path2id("grandparent"), b"a")
206
self.assertEqual(btree.path2id("grandparent/parent"), b"b")
207
self.assertEqual(btree.path2id("grandparent/parent/file"), b"c")
209
self.assertIs(btree.path2id("grandparent2"), None)
210
self.assertIs(btree.path2id("grandparent2/parent"), None)
211
self.assertIs(btree.path2id("grandparent2/parent/file"), None)
187
213
btree.note_rename("grandparent", "grandparent2")
188
self.assertTrue(btree.old_path("grandparent") is None)
189
self.assertTrue(btree.old_path("grandparent/parent") is None)
190
self.assertTrue(btree.old_path("grandparent/parent/file") is None)
192
self.assertEqual(btree.id2path("a"), "grandparent2")
193
self.assertEqual(btree.id2path("b"), "grandparent2/parent")
194
self.assertEqual(btree.id2path("c"), "grandparent2/parent/file")
196
self.assertEqual(btree.path2id("grandparent2"), "a")
197
self.assertEqual(btree.path2id("grandparent2/parent"), "b")
198
self.assertEqual(btree.path2id("grandparent2/parent/file"), "c")
214
self.assertIs(btree.old_path("grandparent"), None)
215
self.assertIs(btree.old_path("grandparent/parent"), None)
216
self.assertIs(btree.old_path("grandparent/parent/file"), None)
218
self.assertEqual(btree.id2path(b"a"), "grandparent2")
219
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent")
220
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent/file")
222
self.assertEqual(btree.path2id("grandparent2"), b"a")
223
self.assertEqual(btree.path2id("grandparent2/parent"), b"b")
224
self.assertEqual(btree.path2id("grandparent2/parent/file"), b"c")
200
226
self.assertTrue(btree.path2id("grandparent") is None)
201
227
self.assertTrue(btree.path2id("grandparent/parent") is None)
202
228
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
204
230
btree.note_rename("grandparent/parent", "grandparent2/parent2")
205
self.assertEqual(btree.id2path("a"), "grandparent2")
206
self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
207
self.assertEqual(btree.id2path("c"), "grandparent2/parent2/file")
231
self.assertEqual(btree.id2path(b"a"), "grandparent2")
232
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
233
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent2/file")
209
self.assertEqual(btree.path2id("grandparent2"), "a")
210
self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
211
self.assertEqual(btree.path2id("grandparent2/parent2/file"), "c")
235
self.assertEqual(btree.path2id("grandparent2"), b"a")
236
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
237
self.assertEqual(btree.path2id("grandparent2/parent2/file"), b"c")
213
239
self.assertTrue(btree.path2id("grandparent2/parent") is None)
214
240
self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
216
242
btree.note_rename("grandparent/parent/file",
217
243
"grandparent2/parent2/file2")
218
self.assertEqual(btree.id2path("a"), "grandparent2")
219
self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
220
self.assertEqual(btree.id2path("c"), "grandparent2/parent2/file2")
244
self.assertEqual(btree.id2path(b"a"), "grandparent2")
245
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
246
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent2/file2")
222
self.assertEqual(btree.path2id("grandparent2"), "a")
223
self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
224
self.assertEqual(btree.path2id("grandparent2/parent2/file2"), "c")
248
self.assertEqual(btree.path2id("grandparent2"), b"a")
249
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
250
self.assertEqual(btree.path2id("grandparent2/parent2/file2"), b"c")
226
252
self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
230
256
btree = self.make_tree_1()[0]
231
257
btree.note_rename("grandparent/parent/file",
232
258
"grandparent/alt_parent/file")
233
self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
234
self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
259
self.assertEqual(btree.id2path(b"c"), "grandparent/alt_parent/file")
260
self.assertEqual(btree.path2id("grandparent/alt_parent/file"), b"c")
235
261
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
237
263
def unified_diff(self, old, new):
239
265
diff.internal_diff("old", old, "new", new, out)
241
267
return out.read()
243
269
def make_tree_2(self):
244
270
btree = self.make_tree_1()[0]
245
271
btree.note_rename("grandparent/parent/file",
246
272
"grandparent/alt_parent/file")
247
self.assertTrue(btree.id2path("e") is None)
248
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
249
btree.note_id("e", "grandparent/parent/file")
273
self.assertTrue(btree.id2path(b"e") is None)
274
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
275
btree.note_id(b"e", "grandparent/parent/file")
252
278
def test_adds(self):
253
279
"""File/inventory adds"""
254
280
btree = self.make_tree_2()
255
add_patch = self.unified_diff([], ["Extra cheese\n"])
281
add_patch = self.unified_diff([], [b"Extra cheese\n"])
256
282
btree.note_patch("grandparent/parent/file", add_patch)
257
btree.note_id('f', 'grandparent/parent/symlink', kind='symlink')
283
btree.note_id(b'f', 'grandparent/parent/symlink', kind='symlink')
258
284
btree.note_target('grandparent/parent/symlink', 'venus')
259
285
self.adds_test(btree)
261
287
def adds_test(self, btree):
262
self.assertEqual(btree.id2path("e"), "grandparent/parent/file")
263
self.assertEqual(btree.path2id("grandparent/parent/file"), "e")
264
self.assertEqual(btree.get_file("e").read(), "Extra cheese\n")
265
self.assertEqual(btree.get_symlink_target('f'), 'venus')
288
self.assertEqual(btree.id2path(b"e"), "grandparent/parent/file")
289
self.assertEqual(btree.path2id("grandparent/parent/file"), b"e")
290
with btree.get_file("grandparent/parent/file") as f:
291
self.assertEqual(f.read(), b"Extra cheese\n")
293
btree.get_symlink_target('grandparent/parent/symlink'), 'venus')
267
295
def test_adds2(self):
268
296
"""File/inventory adds, with patch-compatibile renames"""
269
297
btree = self.make_tree_2()
270
298
btree.contents_by_id = False
271
add_patch = self.unified_diff(["Hello\n"], ["Extra cheese\n"])
299
add_patch = self.unified_diff([b"Hello\n"], [b"Extra cheese\n"])
272
300
btree.note_patch("grandparent/parent/file", add_patch)
273
btree.note_id('f', 'grandparent/parent/symlink', kind='symlink')
301
btree.note_id(b'f', 'grandparent/parent/symlink', kind='symlink')
274
302
btree.note_target('grandparent/parent/symlink', 'venus')
275
303
self.adds_test(btree)
277
305
def make_tree_3(self):
278
306
btree, mtree = self.make_tree_1()
279
mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
307
mtree.add_file(b"e", "grandparent/parent/topping", b"Anchovies\n")
280
308
btree.note_rename("grandparent/parent/file",
281
309
"grandparent/alt_parent/file")
282
310
btree.note_rename("grandparent/parent/topping",
286
314
def get_file_test(self, btree):
287
self.assertEqual(btree.get_file("e").read(), "Lemon\n")
288
self.assertEqual(btree.get_file("c").read(), "Hello\n")
315
with btree.get_file(btree.id2path(b"e")) as f:
316
self.assertEqual(f.read(), b"Lemon\n")
317
with btree.get_file(btree.id2path(b"c")) as f:
318
self.assertEqual(f.read(), b"Hello\n")
290
320
def test_get_file(self):
291
321
"""Get file contents"""
292
322
btree = self.make_tree_3()
293
mod_patch = self.unified_diff(["Anchovies\n"], ["Lemon\n"])
323
mod_patch = self.unified_diff([b"Anchovies\n"], [b"Lemon\n"])
294
324
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
295
325
self.get_file_test(btree)
297
327
def test_get_file2(self):
298
"""Get file contents, with patch-compatibile renames"""
328
"""Get file contents, with patch-compatible renames"""
299
329
btree = self.make_tree_3()
300
330
btree.contents_by_id = False
301
mod_patch = self.unified_diff([], ["Lemon\n"])
331
mod_patch = self.unified_diff([], [b"Lemon\n"])
302
332
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
303
mod_patch = self.unified_diff([], ["Hello\n"])
333
mod_patch = self.unified_diff([], [b"Hello\n"])
304
334
btree.note_patch("grandparent/alt_parent/file", mod_patch)
305
335
self.get_file_test(btree)
307
337
def test_delete(self):
308
338
"Deletion by bundle"
309
339
btree = self.make_tree_1()[0]
310
self.assertEqual(btree.get_file("c").read(), "Hello\n")
340
with btree.get_file(btree.id2path(b"c")) as f:
341
self.assertEqual(f.read(), b"Hello\n")
311
342
btree.note_deletion("grandparent/parent/file")
312
self.assertTrue(btree.id2path("c") is None)
313
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
343
self.assertTrue(btree.id2path(b"c") is None)
344
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
315
346
def sorted_ids(self, tree):
347
ids = sorted(tree.all_file_ids())
320
350
def test_iteration(self):
321
351
"""Ensure that iteration through ids works properly"""
322
352
btree = self.make_tree_1()[0]
323
353
self.assertEqual(self.sorted_ids(btree),
324
[inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
354
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
325
355
btree.note_deletion("grandparent/parent/file")
326
btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
356
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
327
357
btree.note_last_changed("grandparent/alt_parent/fool",
328
358
"revisionidiguess")
329
359
self.assertEqual(self.sorted_ids(btree),
330
[inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
360
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
333
363
class BundleTester1(tests.TestCaseWithTransport):
574
602
self.tree1 = self.make_branch_and_tree('b1')
575
603
self.b1 = self.tree1.branch
577
self.build_tree_contents([('b1/one', 'one\n')])
578
self.tree1.add('one', 'one-id')
579
self.tree1.set_root_id('root-id')
580
self.tree1.commit('add one', rev_id='a@cset-0-1')
605
self.build_tree_contents([('b1/one', b'one\n')])
606
self.tree1.add('one', b'one-id')
607
self.tree1.set_root_id(b'root-id')
608
self.tree1.commit('add one', rev_id=b'a@cset-0-1')
582
bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
610
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-1')
584
612
# Make sure we can handle files with spaces, tabs, other
585
613
# bogus characters
586
614
self.build_tree([
589
, 'b1/dir/filein subdir.c'
590
, 'b1/dir/WithCaps.txt'
591
, 'b1/dir/ pre space'
594
, 'b1/sub/sub/nonempty.txt'
596
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
597
('b1/dir/nolastnewline.txt', 'bloop')])
615
'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'
617
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
618
('b1/dir/nolastnewline.txt', b'bloop')])
598
619
tt = TreeTransform(self.tree1)
599
tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
620
tt.new_file('executable', tt.root, [b'#!/bin/sh\n'], b'exe-1', True)
601
622
# have to fix length of file-id so that we can predictably rewrite
602
623
# a (length-prefixed) record containing it later.
603
self.tree1.add('with space.txt', 'withspace-id')
624
self.tree1.add('with space.txt', b'withspace-id')
606
, 'dir/filein subdir.c'
609
, 'dir/nolastnewline.txt'
612
, 'sub/sub/nonempty.txt'
613
, 'sub/sub/emptyfile.txt'
615
self.tree1.commit('add whitespace', rev_id='a@cset-0-2')
626
'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'
628
self.tree1.commit('add whitespace', rev_id=b'a@cset-0-2')
617
bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
630
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
619
632
# Check a rollup bundle
620
bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
633
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-2')
622
635
# Now delete entries
623
636
self.tree1.remove(
624
['sub/sub/nonempty.txt'
625
, 'sub/sub/emptyfile.txt'
637
['sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt', 'sub/sub'
628
639
tt = TreeTransform(self.tree1)
629
trans_id = tt.trans_id_tree_file_id('exe-1')
640
trans_id = tt.trans_id_tree_path('executable')
630
641
tt.set_executability(False, trans_id)
632
self.tree1.commit('removed', rev_id='a@cset-0-3')
643
self.tree1.commit('removed', rev_id=b'a@cset-0-3')
634
bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
645
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
635
646
self.assertRaises((errors.TestamentMismatch,
636
errors.VersionedFileInvalidChecksum,
637
errors.BadBundle), self.get_invalid_bundle,
638
'a@cset-0-2', 'a@cset-0-3')
647
errors.VersionedFileInvalidChecksum,
648
errors.BadBundle), self.get_invalid_bundle,
649
b'a@cset-0-2', b'a@cset-0-3')
639
650
# Check a rollup bundle
640
bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
651
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
642
653
# Now move the directory
643
654
self.tree1.rename_one('dir', 'sub/dir')
644
self.tree1.commit('rename dir', rev_id='a@cset-0-4')
655
self.tree1.commit('rename dir', rev_id=b'a@cset-0-4')
646
bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
657
bundle = self.get_valid_bundle(b'a@cset-0-3', b'a@cset-0-4')
647
658
# Check a rollup bundle
648
bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
659
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-4')
651
open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
652
open('b1/sub/dir/ pre space', 'ab').write(
653
'\r\nAdding some\r\nDOS format lines\r\n')
654
open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
662
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
663
f.write(b'\nAdding some text\n')
664
with open('b1/sub/dir/ pre space', 'ab') as f:
666
b'\r\nAdding some\r\nDOS format lines\r\n')
667
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
655
669
self.tree1.rename_one('sub/dir/ pre space',
656
670
'sub/ start space')
657
self.tree1.commit('Modified files', rev_id='a@cset-0-5')
658
bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
671
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
672
bundle = self.get_valid_bundle(b'a@cset-0-4', b'a@cset-0-5')
660
674
self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
661
675
self.tree1.rename_one('with space.txt', 'WithCaps.txt')
662
676
self.tree1.rename_one('temp', 'with space.txt')
663
self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
677
self.tree1.commit(u'swap filenames', rev_id=b'a@cset-0-6',
665
bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
666
other = self.get_checkout('a@cset-0-5')
679
bundle = self.get_valid_bundle(b'a@cset-0-5', b'a@cset-0-6')
680
other = self.get_checkout(b'a@cset-0-5')
667
681
tree1_inv = get_inventory_text(self.tree1.branch.repository,
669
683
tree2_inv = get_inventory_text(other.branch.repository,
671
685
self.assertEqualDiff(tree1_inv, tree2_inv)
672
686
other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
673
other.commit('rename file', rev_id='a@cset-0-6b')
687
other.commit('rename file', rev_id=b'a@cset-0-6b')
674
688
self.tree1.merge_from_branch(other.branch)
675
self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
689
self.tree1.commit(u'Merge', rev_id=b'a@cset-0-7',
677
bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
691
bundle = self.get_valid_bundle(b'a@cset-0-6', b'a@cset-0-7')
679
693
def _test_symlink_bundle(self, link_name, link_target, new_link_target):
682
self.requireFeature(tests.SymlinkFeature)
696
self.requireFeature(features.SymlinkFeature)
683
697
self.tree1 = self.make_branch_and_tree('b1')
684
698
self.b1 = self.tree1.branch
686
700
tt = TreeTransform(self.tree1)
687
701
tt.new_symlink(link_name, tt.root, link_target, link_id)
689
self.tree1.commit('add symlink', rev_id='l@cset-0-1')
690
bundle = self.get_valid_bundle('null:', 'l@cset-0-1')
691
if getattr(bundle ,'revision_tree', None) is not None:
703
self.tree1.commit('add symlink', rev_id=b'l@cset-0-1')
704
bundle = self.get_valid_bundle(b'null:', b'l@cset-0-1')
705
if getattr(bundle, 'revision_tree', None) is not None:
692
706
# Not all bundle formats supports revision_tree
693
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-1')
694
self.assertEqual(link_target, bund_tree.get_symlink_target(link_id))
707
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
709
link_target, bund_tree.get_symlink_target(link_name))
696
711
tt = TreeTransform(self.tree1)
697
trans_id = tt.trans_id_tree_file_id(link_id)
712
trans_id = tt.trans_id_tree_path(link_name)
698
713
tt.adjust_path('link2', tt.root, trans_id)
699
714
tt.delete_contents(trans_id)
700
715
tt.create_symlink(new_link_target, trans_id)
702
self.tree1.commit('rename and change symlink', rev_id='l@cset-0-2')
703
bundle = self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
704
if getattr(bundle ,'revision_tree', None) is not None:
717
self.tree1.commit('rename and change symlink', rev_id=b'l@cset-0-2')
718
bundle = self.get_valid_bundle(b'l@cset-0-1', b'l@cset-0-2')
719
if getattr(bundle, 'revision_tree', None) is not None:
705
720
# Not all bundle formats supports revision_tree
706
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
721
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-2')
707
722
self.assertEqual(new_link_target,
708
bund_tree.get_symlink_target(link_id))
723
bund_tree.get_symlink_target('link2'))
710
725
tt = TreeTransform(self.tree1)
711
trans_id = tt.trans_id_tree_file_id(link_id)
726
trans_id = tt.trans_id_tree_path('link2')
712
727
tt.delete_contents(trans_id)
713
728
tt.create_symlink('jupiter', trans_id)
715
self.tree1.commit('just change symlink target', rev_id='l@cset-0-3')
716
bundle = self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
730
self.tree1.commit('just change symlink target', rev_id=b'l@cset-0-3')
731
bundle = self.get_valid_bundle(b'l@cset-0-2', b'l@cset-0-3')
718
733
tt = TreeTransform(self.tree1)
719
trans_id = tt.trans_id_tree_file_id(link_id)
734
trans_id = tt.trans_id_tree_path('link2')
720
735
tt.delete_contents(trans_id)
722
self.tree1.commit('Delete symlink', rev_id='l@cset-0-4')
723
bundle = self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
737
self.tree1.commit('Delete symlink', rev_id=b'l@cset-0-4')
738
bundle = self.get_valid_bundle(b'l@cset-0-3', b'l@cset-0-4')
725
740
def test_symlink_bundle(self):
726
741
self._test_symlink_bundle('link', 'bar/foo', 'mars')
728
743
def test_unicode_symlink_bundle(self):
729
self.requireFeature(tests.UnicodeFilenameFeature)
744
self.requireFeature(features.UnicodeFilenameFeature)
730
745
self._test_symlink_bundle(u'\N{Euro Sign}link',
731
746
u'bar/\N{Euro Sign}foo',
732
747
u'mars\N{Euro Sign}')
737
752
tt = TreeTransform(self.tree1)
740
tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
741
tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
755
tt.new_file('file', tt.root, [
756
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
757
tt.new_file('file2', tt.root, [b'\x01\n\x02\r\x03\n\x04\r\xff'],
744
self.tree1.commit('add binary', rev_id='b@cset-0-1')
745
self.get_valid_bundle('null:', 'b@cset-0-1')
760
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
761
self.get_valid_bundle(b'null:', b'b@cset-0-1')
748
764
tt = TreeTransform(self.tree1)
749
trans_id = tt.trans_id_tree_file_id('binary-1')
765
trans_id = tt.trans_id_tree_path('file')
750
766
tt.delete_contents(trans_id)
752
self.tree1.commit('delete binary', rev_id='b@cset-0-2')
753
self.get_valid_bundle('b@cset-0-1', 'b@cset-0-2')
768
self.tree1.commit('delete binary', rev_id=b'b@cset-0-2')
769
self.get_valid_bundle(b'b@cset-0-1', b'b@cset-0-2')
755
771
# Rename & modify
756
772
tt = TreeTransform(self.tree1)
757
trans_id = tt.trans_id_tree_file_id('binary-2')
773
trans_id = tt.trans_id_tree_path('file2')
758
774
tt.adjust_path('file3', tt.root, trans_id)
759
775
tt.delete_contents(trans_id)
760
tt.create_file('file\rcontents\x00\n\x00', trans_id)
776
tt.create_file([b'file\rcontents\x00\n\x00'], trans_id)
762
self.tree1.commit('rename and modify binary', rev_id='b@cset-0-3')
763
self.get_valid_bundle('b@cset-0-2', 'b@cset-0-3')
778
self.tree1.commit('rename and modify binary', rev_id=b'b@cset-0-3')
779
self.get_valid_bundle(b'b@cset-0-2', b'b@cset-0-3')
766
782
tt = TreeTransform(self.tree1)
767
trans_id = tt.trans_id_tree_file_id('binary-2')
783
trans_id = tt.trans_id_tree_path('file3')
768
784
tt.delete_contents(trans_id)
769
tt.create_file('\x00file\rcontents', trans_id)
785
tt.create_file([b'\x00file\rcontents'], trans_id)
771
self.tree1.commit('just modify binary', rev_id='b@cset-0-4')
772
self.get_valid_bundle('b@cset-0-3', 'b@cset-0-4')
787
self.tree1.commit('just modify binary', rev_id=b'b@cset-0-4')
788
self.get_valid_bundle(b'b@cset-0-3', b'b@cset-0-4')
775
self.get_valid_bundle('null:', 'b@cset-0-4')
791
self.get_valid_bundle(b'null:', b'b@cset-0-4')
777
793
def test_last_modified(self):
778
794
self.tree1 = self.make_branch_and_tree('b1')
779
795
self.b1 = self.tree1.branch
780
796
tt = TreeTransform(self.tree1)
781
tt.new_file('file', tt.root, 'file', 'file')
797
tt.new_file('file', tt.root, [b'file'], b'file')
783
self.tree1.commit('create file', rev_id='a@lmod-0-1')
799
self.tree1.commit('create file', rev_id=b'a@lmod-0-1')
785
801
tt = TreeTransform(self.tree1)
786
trans_id = tt.trans_id_tree_file_id('file')
802
trans_id = tt.trans_id_tree_path('file')
787
803
tt.delete_contents(trans_id)
788
tt.create_file('file2', trans_id)
804
tt.create_file([b'file2'], trans_id)
790
self.tree1.commit('modify text', rev_id='a@lmod-0-2a')
806
self.tree1.commit('modify text', rev_id=b'a@lmod-0-2a')
792
other = self.get_checkout('a@lmod-0-1')
808
other = self.get_checkout(b'a@lmod-0-1')
793
809
tt = TreeTransform(other)
794
trans_id = tt.trans_id_tree_file_id('file')
810
trans_id = tt.trans_id_tree_path('file2')
795
811
tt.delete_contents(trans_id)
796
tt.create_file('file2', trans_id)
812
tt.create_file([b'file2'], trans_id)
798
other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
814
other.commit('modify text in another tree', rev_id=b'a@lmod-0-2b')
799
815
self.tree1.merge_from_branch(other.branch)
800
self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
816
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-3',
802
self.tree1.commit(u'Merge', rev_id='a@lmod-0-4')
803
bundle = self.get_valid_bundle('a@lmod-0-2a', 'a@lmod-0-4')
818
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-4')
819
bundle = self.get_valid_bundle(b'a@lmod-0-2a', b'a@lmod-0-4')
805
821
def test_hide_history(self):
806
822
self.tree1 = self.make_branch_and_tree('b1')
807
823
self.b1 = self.tree1.branch
809
open('b1/one', 'wb').write('one\n')
825
with open('b1/one', 'wb') as f:
810
827
self.tree1.add('one')
811
self.tree1.commit('add file', rev_id='a@cset-0-1')
812
open('b1/one', 'wb').write('two\n')
813
self.tree1.commit('modify', rev_id='a@cset-0-2')
814
open('b1/one', 'wb').write('three\n')
815
self.tree1.commit('modify', rev_id='a@cset-0-3')
816
bundle_file = StringIO()
817
rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
818
'a@cset-0-1', bundle_file, format=self.format)
819
self.assertNotContainsRe(bundle_file.getvalue(), '\btwo\b')
820
self.assertContainsRe(self.get_raw(bundle_file), 'one')
821
self.assertContainsRe(self.get_raw(bundle_file), 'three')
828
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
829
with open('b1/one', 'wb') as f:
831
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
832
with open('b1/one', 'wb') as f:
834
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
835
bundle_file = BytesIO()
836
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
837
b'a@cset-0-1', bundle_file, format=self.format)
838
self.assertNotContainsRe(bundle_file.getvalue(), b'\btwo\b')
839
self.assertContainsRe(self.get_raw(bundle_file), b'one')
840
self.assertContainsRe(self.get_raw(bundle_file), b'three')
823
842
def test_bundle_same_basis(self):
824
843
"""Ensure using the basis as the target doesn't cause an error"""
825
844
self.tree1 = self.make_branch_and_tree('b1')
826
self.tree1.commit('add file', rev_id='a@cset-0-1')
827
bundle_file = StringIO()
828
rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-1',
829
'a@cset-0-1', bundle_file)
845
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
846
bundle_file = BytesIO()
847
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-1',
848
b'a@cset-0-1', bundle_file)
832
851
def get_raw(bundle_file):
833
852
return bundle_file.getvalue()
835
854
def test_unicode_bundle(self):
836
self.requireFeature(tests.UnicodeFilenameFeature)
855
self.requireFeature(features.UnicodeFilenameFeature)
837
856
# Handle international characters
839
858
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
842
861
self.b1 = self.tree1.branch
844
863
f.write((u'A file\n'
845
u'With international man of mystery\n'
846
u'William Dod\xe9\n').encode('utf-8'))
864
u'With international man of mystery\n'
865
u'William Dod\xe9\n').encode('utf-8'))
849
self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
868
self.tree1.add([u'with Dod\N{Euro Sign}'], [b'withdod-id'])
850
869
self.tree1.commit(u'i18n commit from William Dod\xe9',
851
rev_id='i18n-1', committer=u'William Dod\xe9')
870
rev_id=b'i18n-1', committer=u'William Dod\xe9')
854
bundle = self.get_valid_bundle('null:', 'i18n-1')
873
bundle = self.get_valid_bundle(b'null:', b'i18n-1')
857
876
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
858
877
f.write(u'Modified \xb5\n'.encode('utf8'))
860
self.tree1.commit(u'modified', rev_id='i18n-2')
879
self.tree1.commit(u'modified', rev_id=b'i18n-2')
862
bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
881
bundle = self.get_valid_bundle(b'i18n-1', b'i18n-2')
865
884
self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
866
self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
885
self.tree1.commit(u'renamed, the new i18n man', rev_id=b'i18n-3',
867
886
committer=u'Erik B\xe5gfors')
869
bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
888
bundle = self.get_valid_bundle(b'i18n-2', b'i18n-3')
872
891
self.tree1.remove([u'B\N{Euro Sign}gfors'])
873
self.tree1.commit(u'removed', rev_id='i18n-4')
892
self.tree1.commit(u'removed', rev_id=b'i18n-4')
875
bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
894
bundle = self.get_valid_bundle(b'i18n-3', b'i18n-4')
878
bundle = self.get_valid_bundle('null:', 'i18n-4')
897
bundle = self.get_valid_bundle(b'null:', b'i18n-4')
881
899
def test_whitespace_bundle(self):
882
900
if sys.platform in ('win32', 'cygwin'):
941
960
def test_bundle_root_id(self):
942
961
self.tree1 = self.make_branch_and_tree('b1')
943
962
self.b1 = self.tree1.branch
944
self.tree1.commit('message', rev_id='revid1')
945
bundle = self.get_valid_bundle('null:', 'revid1')
946
tree = self.get_bundle_tree(bundle, 'revid1')
947
self.assertEqual('revid1', tree.inventory.root.revision)
963
self.tree1.commit('message', rev_id=b'revid1')
964
bundle = self.get_valid_bundle(b'null:', b'revid1')
965
tree = self.get_bundle_tree(bundle, b'revid1')
966
root_revision = tree.get_file_revision(u'')
967
self.assertEqual(b'revid1', root_revision)
949
969
def test_install_revisions(self):
950
970
self.tree1 = self.make_branch_and_tree('b1')
951
971
self.b1 = self.tree1.branch
952
self.tree1.commit('message', rev_id='rev2a')
953
bundle = self.get_valid_bundle('null:', 'rev2a')
972
self.tree1.commit('message', rev_id=b'rev2a')
973
bundle = self.get_valid_bundle(b'null:', b'rev2a')
954
974
branch2 = self.make_branch('b2')
955
self.assertFalse(branch2.repository.has_revision('rev2a'))
975
self.assertFalse(branch2.repository.has_revision(b'rev2a'))
956
976
target_revision = bundle.install_revisions(branch2.repository)
957
self.assertTrue(branch2.repository.has_revision('rev2a'))
958
self.assertEqual('rev2a', target_revision)
977
self.assertTrue(branch2.repository.has_revision(b'rev2a'))
978
self.assertEqual(b'rev2a', target_revision)
960
980
def test_bundle_empty_property(self):
961
981
"""Test serializing revision properties with an empty value."""
962
982
tree = self.make_branch_and_memory_tree('tree')
963
983
tree.lock_write()
964
984
self.addCleanup(tree.unlock)
965
tree.add([''], ['TREE_ROOT'])
966
tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
985
tree.add([''], [b'TREE_ROOT'])
986
tree.commit('One', revprops={u'one': 'two',
987
u'empty': ''}, rev_id=b'rev1')
967
988
self.b1 = tree.branch
968
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
989
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
969
990
bundle = read_bundle(bundle_sio)
970
991
revision_info = bundle.revisions[0]
971
self.assertEqual('rev1', revision_info.revision_id)
992
self.assertEqual(b'rev1', revision_info.revision_id)
972
993
rev = revision_info.as_revision()
973
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
994
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
976
997
def test_bundle_sorted_properties(self):
997
1018
tree.lock_write()
998
1019
self.addCleanup(tree.unlock)
1000
tree.add([''], ['TREE_ROOT'])
1021
tree.add([''], [b'TREE_ROOT'])
1001
1022
# Revisions themselves do not require anything about revision property
1002
1023
# keys, other than that they are a basestring, and do not contain
1004
1025
# However, Testaments assert than they are str(), and thus should not
1006
tree.commit('One', rev_id='rev1',
1007
revprops={'omega':u'\u03a9', 'alpha':u'\u03b1'})
1027
tree.commit('One', rev_id=b'rev1',
1028
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1008
1029
self.b1 = tree.branch
1009
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
1030
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1010
1031
bundle = read_bundle(bundle_sio)
1011
1032
revision_info = bundle.revisions[0]
1012
self.assertEqual('rev1', revision_info.revision_id)
1033
self.assertEqual(b'rev1', revision_info.revision_id)
1013
1034
rev = revision_info.as_revision()
1014
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1015
'alpha':u'\u03b1'}, rev.properties)
1035
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1036
'alpha': u'\u03b1'}, rev.properties)
1017
1038
def test_bundle_with_ghosts(self):
1018
1039
tree = self.make_branch_and_tree('tree')
1019
1040
self.b1 = tree.branch
1020
self.build_tree_contents([('tree/file', 'content1')])
1041
self.build_tree_contents([('tree/file', b'content1')])
1021
1042
tree.add(['file'])
1022
1043
tree.commit('rev1')
1023
self.build_tree_contents([('tree/file', 'content2')])
1024
tree.add_parent_tree_id('ghost')
1025
tree.commit('rev2', rev_id='rev2')
1026
bundle = self.get_valid_bundle('null:', 'rev2')
1044
self.build_tree_contents([('tree/file', b'content2')])
1045
tree.add_parent_tree_id(b'ghost')
1046
tree.commit('rev2', rev_id=b'rev2')
1047
bundle = self.get_valid_bundle(b'null:', b'rev2')
1028
1049
def make_simple_tree(self, format=None):
1029
1050
tree = self.make_branch_and_tree('b1', format=format)
1035
1056
def test_across_serializers(self):
1036
1057
tree = self.make_simple_tree('knit')
1037
tree.commit('hello', rev_id='rev1')
1038
tree.commit('hello', rev_id='rev2')
1039
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1058
tree.commit('hello', rev_id=b'rev1')
1059
tree.commit('hello', rev_id=b'rev2')
1060
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1040
1061
repo = self.make_repository('repo', format='dirstate-with-subtree')
1041
1062
bundle.install_revisions(repo)
1042
inv_text = repo._get_inventory_xml('rev2')
1043
self.assertNotContainsRe(inv_text, 'format="5"')
1044
self.assertContainsRe(inv_text, 'format="7"')
1063
inv_text = repo._get_inventory_xml(b'rev2')
1064
self.assertNotContainsRe(inv_text, b'format="5"')
1065
self.assertContainsRe(inv_text, b'format="7"')
1046
1067
def make_repo_with_installed_revisions(self):
1047
1068
tree = self.make_simple_tree('knit')
1048
tree.commit('hello', rev_id='rev1')
1049
tree.commit('hello', rev_id='rev2')
1050
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1069
tree.commit('hello', rev_id=b'rev1')
1070
tree.commit('hello', rev_id=b'rev2')
1071
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1051
1072
repo = self.make_repository('repo', format='dirstate-with-subtree')
1052
1073
bundle.install_revisions(repo)
1055
1076
def test_across_models(self):
1056
1077
repo = self.make_repo_with_installed_revisions()
1057
inv = repo.get_inventory('rev2')
1058
self.assertEqual('rev2', inv.root.revision)
1078
inv = repo.get_inventory(b'rev2')
1079
self.assertEqual(b'rev2', inv.root.revision)
1059
1080
root_id = inv.root.file_id
1060
1081
repo.lock_read()
1061
1082
self.addCleanup(repo.unlock)
1062
self.assertEqual({(root_id, 'rev1'):(),
1063
(root_id, 'rev2'):((root_id, 'rev1'),)},
1064
repo.texts.get_parent_map([(root_id, 'rev1'), (root_id, 'rev2')]))
1083
self.assertEqual({(root_id, b'rev1'): (),
1084
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1085
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1066
1087
def test_inv_hash_across_serializers(self):
1067
1088
repo = self.make_repo_with_installed_revisions()
1068
recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1069
xml = repo._get_inventory_xml('rev2')
1089
recorded_inv_sha1 = repo.get_revision(b'rev2').inventory_sha1
1090
xml = repo._get_inventory_xml(b'rev2')
1070
1091
self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1072
1093
def test_across_models_incompatible(self):
1073
1094
tree = self.make_simple_tree('dirstate-with-subtree')
1074
tree.commit('hello', rev_id='rev1')
1075
tree.commit('hello', rev_id='rev2')
1095
tree.commit('hello', rev_id=b'rev1')
1096
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1098
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1078
1099
except errors.IncompatibleBundleFormat:
1079
1100
raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1080
1101
repo = self.make_repository('repo', format='knit')
1081
1102
bundle.install_revisions(repo)
1083
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1104
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1084
1105
self.assertRaises(errors.IncompatibleRevision,
1085
1106
bundle.install_revisions, repo)
1087
1108
def test_get_merge_request(self):
1088
1109
tree = self.make_simple_tree()
1089
tree.commit('hello', rev_id='rev1')
1090
tree.commit('hello', rev_id='rev2')
1091
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1110
tree.commit('hello', rev_id=b'rev1')
1111
tree.commit('hello', rev_id=b'rev2')
1112
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1092
1113
result = bundle.get_merge_request(tree.branch.repository)
1093
self.assertEqual((None, 'rev1', 'inapplicable'), result)
1114
self.assertEqual((None, b'rev1', 'inapplicable'), result)
1095
1116
def test_with_subtree(self):
1096
1117
tree = self.make_branch_and_tree('tree',
1116
1137
self.tree1 = self.make_branch_and_tree('tree')
1117
1138
self.b1 = self.tree1.branch
1119
self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1140
self.tree1.commit('Revision/id/with/slashes', rev_id=b'rev/id')
1120
1141
except ValueError:
1121
1142
raise tests.TestSkipped(
1122
1143
"Repository doesn't support revision ids with slashes")
1123
bundle = self.get_valid_bundle('null:', 'rev/id')
1144
bundle = self.get_valid_bundle(b'null:', b'rev/id')
1125
1146
def test_skip_file(self):
1126
1147
"""Make sure we don't accidentally write to the wrong versionedfile"""
1127
1148
self.tree1 = self.make_branch_and_tree('tree')
1128
1149
self.b1 = self.tree1.branch
1129
1150
# rev1 is not present in bundle, done by fetch
1130
self.build_tree_contents([('tree/file2', 'contents1')])
1131
self.tree1.add('file2', 'file2-id')
1132
self.tree1.commit('rev1', rev_id='reva')
1133
self.build_tree_contents([('tree/file3', 'contents2')])
1151
self.build_tree_contents([('tree/file2', b'contents1')])
1152
self.tree1.add('file2', b'file2-id')
1153
self.tree1.commit('rev1', rev_id=b'reva')
1154
self.build_tree_contents([('tree/file3', b'contents2')])
1134
1155
# rev2 is present in bundle, and done by fetch
1135
1156
# having file1 in the bunle causes file1's versionedfile to be opened.
1136
self.tree1.add('file3', 'file3-id')
1137
self.tree1.commit('rev2')
1157
self.tree1.add('file3', b'file3-id')
1158
rev2 = self.tree1.commit('rev2')
1138
1159
# Updating file2 should not cause an attempt to add to file1's vf
1139
target = self.tree1.bzrdir.sprout('target').open_workingtree()
1140
self.build_tree_contents([('tree/file2', 'contents3')])
1141
self.tree1.commit('rev3', rev_id='rev3')
1142
bundle = self.get_valid_bundle('reva', 'rev3')
1160
target = self.tree1.controldir.sprout('target').open_workingtree()
1161
self.build_tree_contents([('tree/file2', b'contents3')])
1162
self.tree1.commit('rev3', rev_id=b'rev3')
1163
bundle = self.get_valid_bundle(b'reva', b'rev3')
1143
1164
if getattr(bundle, 'get_bundle_reader', None) is None:
1144
1165
raise tests.TestSkipped('Bundle format cannot provide reader')
1145
# be sure that file1 comes before file2
1146
for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1149
self.assertNotEqual(f, 'file2-id')
1167
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1170
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1150
1171
bundle.install_revisions(target.branch.repository)
1402
1427
line = bundle_file.readline()
1403
1428
line = bundle_file.readline()
1404
1429
lines = bundle_file.readlines()
1405
return ''.join(lines).decode('bz2')
1430
return bz2.decompress(b''.join(lines))
1407
1432
def test_copy_signatures(self):
1408
1433
tree_a = self.make_branch_and_tree('tree_a')
1410
import bzrlib.commit as commit
1411
oldstrategy = bzrlib.gpg.GPGStrategy
1435
import breezy.commit as commit
1436
oldstrategy = breezy.gpg.GPGStrategy
1412
1437
branch = tree_a.branch
1413
1438
repo_a = branch.repository
1414
tree_a.commit("base", allow_pointless=True, rev_id='A')
1415
self.failIf(branch.repository.has_signature_for_revision_id('A'))
1439
tree_a.commit("base", allow_pointless=True, rev_id=b'A')
1440
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
1417
from bzrlib.testament import Testament
1442
from ..bzr.testament import Testament
1418
1443
# monkey patch gpg signing mechanism
1419
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
1420
new_config = test_commit.MustSignConfig(branch)
1421
commit.Commit(config=new_config).commit(message="base",
1422
allow_pointless=True,
1424
working_tree=tree_a)
1444
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
1445
new_config = test_commit.MustSignConfig()
1446
commit.Commit(config_stack=new_config).commit(message="base",
1447
allow_pointless=True,
1449
working_tree=tree_a)
1425
1451
def sign(text):
1426
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
1427
self.assertTrue(repo_a.has_signature_for_revision_id('B'))
1452
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
1453
self.assertTrue(repo_a.has_signature_for_revision_id(b'B'))
1429
bzrlib.gpg.GPGStrategy = oldstrategy
1455
breezy.gpg.GPGStrategy = oldstrategy
1430
1456
tree_b = self.make_branch_and_tree('tree_b')
1431
1457
repo_b = tree_b.branch.repository
1433
1459
serializer = BundleSerializerV4('4')
1434
serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
1460
with tree_a.lock_read():
1461
serializer.write_bundle(
1462
tree_a.branch.repository, b'B', b'null:', s)
1436
1464
install_bundle(repo_b, serializer.read(s))
1437
self.assertTrue(repo_b.has_signature_for_revision_id('B'))
1438
self.assertEqual(repo_b.get_signature_text('B'),
1439
repo_a.get_signature_text('B'))
1465
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1466
self.assertEqual(repo_b.get_signature_text(b'B'),
1467
repo_a.get_signature_text(b'B'))
1441
1469
# ensure repeat installs are harmless
1442
1470
install_bundle(repo_b, serializer.read(s))
1445
class V4WeaveBundleTester(V4BundleTester):
1447
def bzrdir_format(self):
1451
1473
class V4_2aBundleTester(V4BundleTester):
1453
1475
def bzrdir_format(self):
1480
1502
def make_merged_branch(self):
1481
1503
builder = self.make_branch_builder('source')
1482
1504
builder.start_series()
1483
builder.build_snapshot('a@cset-0-1', None, [
1484
('add', ('', 'root-id', 'directory', None)),
1485
('add', ('file', 'file-id', 'file', 'original content\n')),
1487
builder.build_snapshot('a@cset-0-2a', ['a@cset-0-1'], [
1488
('modify', ('file-id', 'new-content\n')),
1490
builder.build_snapshot('a@cset-0-2b', ['a@cset-0-1'], [
1491
('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1493
builder.build_snapshot('a@cset-0-3', ['a@cset-0-2a', 'a@cset-0-2b'], [
1494
('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1505
builder.build_snapshot(None, [
1506
('add', ('', b'root-id', 'directory', None)),
1507
('add', ('file', b'file-id', 'file', b'original content\n')),
1508
], revision_id=b'a@cset-0-1')
1509
builder.build_snapshot([b'a@cset-0-1'], [
1510
('modify', ('file', b'new-content\n')),
1511
], revision_id=b'a@cset-0-2a')
1512
builder.build_snapshot([b'a@cset-0-1'], [
1513
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1514
], revision_id=b'a@cset-0-2b')
1515
builder.build_snapshot([b'a@cset-0-2a', b'a@cset-0-2b'], [
1516
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1517
], revision_id=b'a@cset-0-3')
1496
1518
builder.finish_series()
1497
1519
self.b1 = builder.get_branch()
1498
1520
self.b1.lock_read()
1513
1535
def test_single_inventory_multiple_parents_as_xml(self):
1514
1536
self.make_merged_branch()
1515
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1537
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1517
1539
reader = v4.BundleReader(sio, stream_input=False)
1518
1540
records = list(reader.iter_records())
1519
1541
self.assertEqual(1, len(records))
1520
1542
(bytes, metadata, repo_kind, revision_id,
1521
1543
file_id) = records[0]
1522
1544
self.assertIs(None, file_id)
1523
self.assertEqual('a@cset-0-3', revision_id)
1545
self.assertEqual(b'a@cset-0-3', revision_id)
1524
1546
self.assertEqual('inventory', repo_kind)
1525
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
'storage_kind': 'mpdiff',
1547
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1548
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1549
b'storage_kind': b'mpdiff',
1529
1551
# We should have an mpdiff that takes some lines from both parents.
1530
1552
self.assertEqualDiff(
1532
'<inventory format="10" revision_id="a@cset-0-3">\n'
1535
'c 1 3 3 2\n', bytes)
1554
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1557
b'c 1 3 3 2\n', bytes)
1537
1559
def test_single_inv_no_parents_as_xml(self):
1538
1560
self.make_merged_branch()
1539
sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1561
sio = self.make_bundle_just_inventories(b'null:', b'a@cset-0-1',
1541
1563
reader = v4.BundleReader(sio, stream_input=False)
1542
1564
records = list(reader.iter_records())
1543
1565
self.assertEqual(1, len(records))
1544
1566
(bytes, metadata, repo_kind, revision_id,
1545
1567
file_id) = records[0]
1546
1568
self.assertIs(None, file_id)
1547
self.assertEqual('a@cset-0-1', revision_id)
1569
self.assertEqual(b'a@cset-0-1', revision_id)
1548
1570
self.assertEqual('inventory', repo_kind)
1549
self.assertEqual({'parents': [],
1550
'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
'storage_kind': 'mpdiff',
1571
self.assertEqual({b'parents': [],
1572
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1573
b'storage_kind': b'mpdiff',
1553
1575
# We should have an mpdiff that takes some lines from both parents.
1554
1576
self.assertEqualDiff(
1556
'<inventory format="10" revision_id="a@cset-0-1">\n'
1557
'<directory file_id="root-id" name=""'
1558
' revision="a@cset-0-1" />\n'
1559
'<file file_id="file-id" name="file" parent_id="root-id"'
1560
' revision="a@cset-0-1"'
1561
' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1562
' text_size="17" />\n'
1578
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1579
b'<directory file_id="root-id" name=""'
1580
b' revision="a@cset-0-1" />\n'
1581
b'<file file_id="file-id" name="file" parent_id="root-id"'
1582
b' revision="a@cset-0-1"'
1583
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1584
b' text_size="17" />\n'
1566
1588
def test_multiple_inventories_as_xml(self):
1567
1589
self.make_merged_branch()
1568
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1569
['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'])
1590
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1591
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1570
1592
reader = v4.BundleReader(sio, stream_input=False)
1571
1593
records = list(reader.iter_records())
1572
1594
self.assertEqual(3, len(records))
1573
1595
revision_ids = [rev_id for b, m, k, rev_id, f in records]
1574
self.assertEqual(['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'],
1596
self.assertEqual([b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'],
1576
1598
metadata_2a = records[0][1]
1577
self.assertEqual({'parents': ['a@cset-0-1'],
1578
'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
'storage_kind': 'mpdiff',
1599
self.assertEqual({b'parents': [b'a@cset-0-1'],
1600
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1601
b'storage_kind': b'mpdiff',
1581
1603
metadata_2b = records[1][1]
1582
self.assertEqual({'parents': ['a@cset-0-1'],
1583
'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
'storage_kind': 'mpdiff',
1604
self.assertEqual({b'parents': [b'a@cset-0-1'],
1605
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1606
b'storage_kind': b'mpdiff',
1586
1608
metadata_3 = records[2][1]
1587
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
'storage_kind': 'mpdiff',
1609
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1610
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1611
b'storage_kind': b'mpdiff',
1591
1613
bytes_2a = records[0][0]
1592
1614
self.assertEqualDiff(
1594
'<inventory format="10" revision_id="a@cset-0-2a">\n'
1598
'<file file_id="file-id" name="file" parent_id="root-id"'
1599
' revision="a@cset-0-2a"'
1600
' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1601
' text_size="12" />\n'
1603
'c 0 3 3 1\n', bytes_2a)
1616
b'<inventory format="10" revision_id="a@cset-0-2a">\n'
1620
b'<file file_id="file-id" name="file" parent_id="root-id"'
1621
b' revision="a@cset-0-2a"'
1622
b' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1623
b' text_size="12" />\n'
1625
b'c 0 3 3 1\n', bytes_2a)
1604
1626
bytes_2b = records[1][0]
1605
1627
self.assertEqualDiff(
1607
'<inventory format="10" revision_id="a@cset-0-2b">\n'
1611
'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1612
' revision="a@cset-0-2b"'
1613
' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1614
' text_size="14" />\n'
1616
'c 0 3 4 1\n', bytes_2b)
1629
b'<inventory format="10" revision_id="a@cset-0-2b">\n'
1633
b'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1634
b' revision="a@cset-0-2b"'
1635
b' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1636
b' text_size="14" />\n'
1638
b'c 0 3 4 1\n', bytes_2b)
1617
1639
bytes_3 = records[2][0]
1618
1640
self.assertEqualDiff(
1620
'<inventory format="10" revision_id="a@cset-0-3">\n'
1623
'c 1 3 3 2\n', bytes_3)
1642
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1645
b'c 1 3 3 2\n', bytes_3)
1625
1647
def test_creating_bundle_preserves_chk_pages(self):
1626
1648
self.make_merged_branch()
1627
target = self.b1.bzrdir.sprout('target',
1628
revision_id='a@cset-0-2a').open_branch()
1629
bundle_txt, rev_ids = self.create_bundle_text('a@cset-0-2a',
1631
self.assertEqual(['a@cset-0-2b', 'a@cset-0-3'], rev_ids)
1649
target = self.b1.controldir.sprout('target',
1650
revision_id=b'a@cset-0-2a').open_branch()
1651
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1653
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1632
1654
bundle = read_bundle(bundle_txt)
1633
1655
target.lock_write()
1634
1656
self.addCleanup(target.unlock)
1635
1657
install_bundle(target.repository, bundle)
1636
inv1 = self.b1.repository.inventories.get_record_stream([
1637
('a@cset-0-3',)], 'unordered',
1638
True).next().get_bytes_as('fulltext')
1639
inv2 = target.repository.inventories.get_record_stream([
1640
('a@cset-0-3',)], 'unordered',
1641
True).next().get_bytes_as('fulltext')
1658
inv1 = next(self.b1.repository.inventories.get_record_stream([
1659
(b'a@cset-0-3',)], 'unordered',
1660
True)).get_bytes_as('fulltext')
1661
inv2 = next(target.repository.inventories.get_record_stream([
1662
(b'a@cset-0-3',)], 'unordered',
1663
True)).get_bytes_as('fulltext')
1642
1664
self.assertEqualDiff(inv1, inv2)
1740
1762
class TestBundleWriterReader(tests.TestCase):
1742
1764
def test_roundtrip_record(self):
1743
fileobj = StringIO()
1744
1766
writer = v4.BundleWriter(fileobj)
1746
writer.add_info_record(foo='bar')
1747
writer._add_record("Record body", {'parents': ['1', '3'],
1748
'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
1768
writer.add_info_record({b'foo': b'bar'})
1769
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1770
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1750
1772
fileobj.seek(0)
1751
1773
reader = v4.BundleReader(fileobj, stream_input=True)
1752
1774
record_iter = reader.iter_records()
1753
record = record_iter.next()
1754
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1755
'info', None, None), record)
1756
record = record_iter.next()
1757
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1758
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1775
record = next(record_iter)
1776
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1777
'info', None, None), record)
1778
record = next(record_iter)
1779
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1780
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1761
1783
def test_roundtrip_record_memory_hungry(self):
1762
fileobj = StringIO()
1763
1785
writer = v4.BundleWriter(fileobj)
1765
writer.add_info_record(foo='bar')
1766
writer._add_record("Record body", {'parents': ['1', '3'],
1767
'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
1787
writer.add_info_record({b'foo': b'bar'})
1788
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1789
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1769
1791
fileobj.seek(0)
1770
1792
reader = v4.BundleReader(fileobj, stream_input=False)
1771
1793
record_iter = reader.iter_records()
1772
record = record_iter.next()
1773
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1774
'info', None, None), record)
1775
record = record_iter.next()
1776
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1777
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1794
record = next(record_iter)
1795
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1796
'info', None, None), record)
1797
record = next(record_iter)
1798
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1799
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1780
1802
def test_encode_name(self):
1781
self.assertEqual('revision/rev1',
1782
v4.BundleWriter.encode_name('revision', 'rev1'))
1783
self.assertEqual('file/rev//1/file-id-1',
1784
v4.BundleWriter.encode_name('file', 'rev/1', 'file-id-1'))
1785
self.assertEqual('info',
1786
v4.BundleWriter.encode_name('info', None, None))
1803
self.assertEqual(b'revision/rev1',
1804
v4.BundleWriter.encode_name('revision', b'rev1'))
1805
self.assertEqual(b'file/rev//1/file-id-1',
1806
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1807
self.assertEqual(b'info',
1808
v4.BundleWriter.encode_name('info', None, None))
1788
1810
def test_decode_name(self):
1789
self.assertEqual(('revision', 'rev1', None),
1790
v4.BundleReader.decode_name('revision/rev1'))
1791
self.assertEqual(('file', 'rev/1', 'file-id-1'),
1792
v4.BundleReader.decode_name('file/rev//1/file-id-1'))
1811
self.assertEqual(('revision', b'rev1', None),
1812
v4.BundleReader.decode_name(b'revision/rev1'))
1813
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1814
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1793
1815
self.assertEqual(('info', None, None),
1794
v4.BundleReader.decode_name('info'))
1816
v4.BundleReader.decode_name(b'info'))
1796
1818
def test_too_many_names(self):
1797
fileobj = StringIO()
1798
1820
writer = v4.BundleWriter(fileobj)
1800
writer.add_info_record(foo='bar')
1801
writer._container.add_bytes_record('blah', ['two', 'names'])
1822
writer.add_info_record({b'foo': b'bar'})
1823
writer._container.add_bytes_record(b'blah', [(b'two', ), (b'names', )])
1803
1825
fileobj.seek(0)
1804
1826
record_iter = v4.BundleReader(fileobj).iter_records()
1805
record = record_iter.next()
1806
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1807
'info', None, None), record)
1808
self.assertRaises(errors.BadBundle, record_iter.next)
1811
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
1813
def test_read_mergeable_skips_local(self):
1814
"""A local bundle named like the URL should not be read.
1816
out, wt = test_read_bundle.create_bundle_file(self)
1817
class FooService(object):
1818
"""A directory service that always returns source"""
1820
def look_up(self, name, url):
1822
directories.register('foo:', FooService, 'Testing directory service')
1823
self.addCleanup(directories.remove, 'foo:')
1824
self.build_tree_contents([('./foo:bar', out.getvalue())])
1825
self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1828
def test_infinite_redirects_are_not_a_bundle(self):
1829
"""If a URL causes TooManyRedirections then NotABundle is raised.
1831
from bzrlib.tests.blackbox.test_push import RedirectingMemoryServer
1832
server = RedirectingMemoryServer()
1833
self.start_server(server)
1834
url = server.get_url() + 'infinite-loop'
1835
self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1837
def test_smart_server_connection_reset(self):
1838
"""If a smart server connection fails during the attempt to read a
1839
bundle, then the ConnectionReset error should be propagated.
1841
# Instantiate a server that will provoke a ConnectionReset
1842
sock_server = _DisconnectingTCPServer()
1843
self.start_server(sock_server)
1844
# We don't really care what the url is since the server will close the
1845
# connection without interpreting it
1846
url = sock_server.get_url()
1847
self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
1850
class _DisconnectingTCPServer(object):
1851
"""A TCP server that immediately closes any connection made to it."""
1853
def start_server(self):
1854
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1855
self.sock.bind(('127.0.0.1', 0))
1857
self.port = self.sock.getsockname()[1]
1858
self.thread = threading.Thread(
1859
name='%s (port %d)' % (self.__class__.__name__, self.port),
1860
target=self.accept_and_close)
1863
def accept_and_close(self):
1864
conn, addr = self.sock.accept()
1865
conn.shutdown(socket.SHUT_RDWR)
1869
return 'bzr://127.0.0.1:%d/' % (self.port,)
1871
def stop_server(self):
1873
# make sure the thread dies by connecting to the listening socket,
1874
# just in case the test failed to do so.
1875
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1876
conn.connect(self.sock.getsockname())
1878
except socket.error:
1827
record = next(record_iter)
1828
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1829
'info', None, None), record)
1830
self.assertRaises(errors.BadBundle, next, record_iter)