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 ..bzr.bundle.apply_bundle import install_bundle, merge_bundle
36
from ..bzr.bundle.bundle_data import BundleTree
37
from ..bzr.bundle.serializer import write_bundle, read_bundle, v09, v4
38
from ..bzr.bundle.serializer.v08 import BundleSerializerV08
39
from ..bzr.bundle.serializer.v09 import BundleSerializerV09
40
from ..bzr.bundle.serializer.v4 import BundleSerializerV4
41
from ..bzr import knitrepo
50
from bzrlib.transform import TreeTransform
46
from ..tree import InterTree
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)
136
def id2path(self, file_id):
137
return self.paths.get(file_id)
139
def has_id(self, file_id):
140
return self.id2path(file_id) is not None
142
def get_file(self, file_id):
144
result.write(self.contents[file_id])
150
def id2path(self, file_id, recurse='down'):
152
return self.paths[file_id]
154
raise errors.NoSuchId(file_id, self)
156
def get_file(self, path):
159
result.write(self.contents[path])
161
raise errors.NoSuchFile(path)
148
def contents_stats(self, file_id):
149
if file_id not in self.contents:
165
def get_file_revision(self, path):
166
return self.inventory.get_entry_by_path(path).revision
168
def get_file_size(self, path):
169
return self.inventory.get_entry_by_path(path).text_size
171
def get_file_sha1(self, path, file_id=None):
172
return self.inventory.get_entry_by_path(path).text_sha1
174
def contents_stats(self, path):
175
if path not in self.contents:
150
176
return None, None
151
text_sha1 = osutils.sha_file(self.get_file(file_id))
152
return text_sha1, len(self.contents[file_id])
177
text_sha1 = osutils.sha_file(self.get_file(path))
178
return text_sha1, len(self.contents[path])
155
181
class BTreeTester(tests.TestCase):
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.assertRaises(errors.NoSuchId, btree.id2path, b"e")
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')
267
def test_adds2(self):
268
"""File/inventory adds, with patch-compatibile renames"""
269
btree = self.make_tree_2()
270
btree.contents_by_id = False
271
add_patch = self.unified_diff(["Hello\n"], ["Extra cheese\n"])
272
btree.note_patch("grandparent/parent/file", add_patch)
273
btree.note_id('f', 'grandparent/parent/symlink', kind='symlink')
274
btree.note_target('grandparent/parent/symlink', 'venus')
275
self.adds_test(btree)
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')
277
295
def make_tree_3(self):
278
296
btree, mtree = self.make_tree_1()
279
mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
297
mtree.add_file(b"e", "grandparent/parent/topping", b"Anchovies\n")
280
298
btree.note_rename("grandparent/parent/file",
281
299
"grandparent/alt_parent/file")
282
300
btree.note_rename("grandparent/parent/topping",
286
304
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")
305
with btree.get_file(btree.id2path(b"e")) as f:
306
self.assertEqual(f.read(), b"Lemon\n")
307
with btree.get_file(btree.id2path(b"c")) as f:
308
self.assertEqual(f.read(), b"Hello\n")
290
310
def test_get_file(self):
291
311
"""Get file contents"""
292
312
btree = self.make_tree_3()
293
mod_patch = self.unified_diff(["Anchovies\n"], ["Lemon\n"])
294
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
295
self.get_file_test(btree)
297
def test_get_file2(self):
298
"""Get file contents, with patch-compatibile renames"""
299
btree = self.make_tree_3()
300
btree.contents_by_id = False
301
mod_patch = self.unified_diff([], ["Lemon\n"])
302
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
303
mod_patch = self.unified_diff([], ["Hello\n"])
304
btree.note_patch("grandparent/alt_parent/file", mod_patch)
313
mod_patch = self.unified_diff([b"Anchovies\n"], [b"Lemon\n"])
314
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
305
315
self.get_file_test(btree)
307
317
def test_delete(self):
308
318
"Deletion by bundle"
309
319
btree = self.make_tree_1()[0]
310
self.assertEqual(btree.get_file("c").read(), "Hello\n")
320
with btree.get_file(btree.id2path(b"c")) as f:
321
self.assertEqual(f.read(), b"Hello\n")
311
322
btree.note_deletion("grandparent/parent/file")
312
self.assertTrue(btree.id2path("c") is None)
313
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
323
self.assertRaises(errors.NoSuchId, btree.id2path, b"c")
324
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
315
326
def sorted_ids(self, tree):
327
ids = sorted(tree.all_file_ids())
320
330
def test_iteration(self):
321
331
"""Ensure that iteration through ids works properly"""
322
332
btree = self.make_tree_1()[0]
323
333
self.assertEqual(self.sorted_ids(btree),
324
[inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
334
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
325
335
btree.note_deletion("grandparent/parent/file")
326
btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
336
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
327
337
btree.note_last_changed("grandparent/alt_parent/fool",
328
338
"revisionidiguess")
329
339
self.assertEqual(self.sorted_ids(btree),
330
[inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
340
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
333
343
class BundleTester1(tests.TestCaseWithTransport):
574
578
self.tree1 = self.make_branch_and_tree('b1')
575
579
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')
581
self.build_tree_contents([('b1/one', b'one\n')])
582
self.tree1.add('one', b'one-id')
583
self.tree1.set_root_id(b'root-id')
584
self.tree1.commit('add one', rev_id=b'a@cset-0-1')
582
bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
586
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-1')
584
588
# Make sure we can handle files with spaces, tabs, other
585
589
# bogus characters
586
590
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')])
598
tt = TreeTransform(self.tree1)
599
tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
591
'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'
593
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
594
('b1/dir/nolastnewline.txt', b'bloop')])
595
tt = self.tree1.get_transform()
596
tt.new_file('executable', tt.root, [b'#!/bin/sh\n'], b'exe-1', True)
601
598
# have to fix length of file-id so that we can predictably rewrite
602
599
# a (length-prefixed) record containing it later.
603
self.tree1.add('with space.txt', 'withspace-id')
600
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')
602
'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'
604
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')
606
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
619
608
# Check a rollup bundle
620
bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
609
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-2')
622
611
# Now delete entries
623
612
self.tree1.remove(
624
['sub/sub/nonempty.txt'
625
, 'sub/sub/emptyfile.txt'
628
tt = TreeTransform(self.tree1)
629
trans_id = tt.trans_id_tree_file_id('exe-1')
613
['sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt', 'sub/sub'
615
tt = self.tree1.get_transform()
616
trans_id = tt.trans_id_tree_path('executable')
630
617
tt.set_executability(False, trans_id)
632
self.tree1.commit('removed', rev_id='a@cset-0-3')
619
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')
621
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
635
622
self.assertRaises((errors.TestamentMismatch,
636
errors.VersionedFileInvalidChecksum,
637
errors.BadBundle), self.get_invalid_bundle,
638
'a@cset-0-2', 'a@cset-0-3')
623
errors.VersionedFileInvalidChecksum,
624
errors.BadBundle), self.get_invalid_bundle,
625
b'a@cset-0-2', b'a@cset-0-3')
639
626
# Check a rollup bundle
640
bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
627
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
642
629
# Now move the directory
643
630
self.tree1.rename_one('dir', 'sub/dir')
644
self.tree1.commit('rename dir', rev_id='a@cset-0-4')
631
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')
633
bundle = self.get_valid_bundle(b'a@cset-0-3', b'a@cset-0-4')
647
634
# Check a rollup bundle
648
bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
635
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')
638
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
639
f.write(b'\nAdding some text\n')
640
with open('b1/sub/dir/ pre space', 'ab') as f:
642
b'\r\nAdding some\r\nDOS format lines\r\n')
643
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
655
645
self.tree1.rename_one('sub/dir/ pre space',
656
646
'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')
647
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
648
bundle = self.get_valid_bundle(b'a@cset-0-4', b'a@cset-0-5')
660
650
self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
661
651
self.tree1.rename_one('with space.txt', 'WithCaps.txt')
662
652
self.tree1.rename_one('temp', 'with space.txt')
663
self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
653
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')
655
bundle = self.get_valid_bundle(b'a@cset-0-5', b'a@cset-0-6')
656
other = self.get_checkout(b'a@cset-0-5')
667
657
tree1_inv = get_inventory_text(self.tree1.branch.repository,
669
659
tree2_inv = get_inventory_text(other.branch.repository,
671
661
self.assertEqualDiff(tree1_inv, tree2_inv)
672
662
other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
673
other.commit('rename file', rev_id='a@cset-0-6b')
663
other.commit('rename file', rev_id=b'a@cset-0-6b')
674
664
self.tree1.merge_from_branch(other.branch)
675
self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
665
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')
667
bundle = self.get_valid_bundle(b'a@cset-0-6', b'a@cset-0-7')
679
669
def _test_symlink_bundle(self, link_name, link_target, new_link_target):
682
self.requireFeature(tests.SymlinkFeature)
672
self.requireFeature(features.SymlinkFeature)
683
673
self.tree1 = self.make_branch_and_tree('b1')
684
674
self.b1 = self.tree1.branch
686
tt = TreeTransform(self.tree1)
676
tt = self.tree1.get_transform()
687
677
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:
679
self.tree1.commit('add symlink', rev_id=b'l@cset-0-1')
680
bundle = self.get_valid_bundle(b'null:', b'l@cset-0-1')
681
if getattr(bundle, 'revision_tree', None) is not None:
692
682
# 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))
683
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
685
link_target, bund_tree.get_symlink_target(link_name))
696
tt = TreeTransform(self.tree1)
697
trans_id = tt.trans_id_tree_file_id(link_id)
687
tt = self.tree1.get_transform()
688
trans_id = tt.trans_id_tree_path(link_name)
698
689
tt.adjust_path('link2', tt.root, trans_id)
699
690
tt.delete_contents(trans_id)
700
691
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:
693
self.tree1.commit('rename and change symlink', rev_id=b'l@cset-0-2')
694
bundle = self.get_valid_bundle(b'l@cset-0-1', b'l@cset-0-2')
695
if getattr(bundle, 'revision_tree', None) is not None:
705
696
# Not all bundle formats supports revision_tree
706
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
697
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-2')
707
698
self.assertEqual(new_link_target,
708
bund_tree.get_symlink_target(link_id))
699
bund_tree.get_symlink_target('link2'))
710
tt = TreeTransform(self.tree1)
711
trans_id = tt.trans_id_tree_file_id(link_id)
701
tt = self.tree1.get_transform()
702
trans_id = tt.trans_id_tree_path('link2')
712
703
tt.delete_contents(trans_id)
713
704
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')
706
self.tree1.commit('just change symlink target', rev_id=b'l@cset-0-3')
707
bundle = self.get_valid_bundle(b'l@cset-0-2', b'l@cset-0-3')
718
tt = TreeTransform(self.tree1)
719
trans_id = tt.trans_id_tree_file_id(link_id)
709
tt = self.tree1.get_transform()
710
trans_id = tt.trans_id_tree_path('link2')
720
711
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')
713
self.tree1.commit('Delete symlink', rev_id=b'l@cset-0-4')
714
bundle = self.get_valid_bundle(b'l@cset-0-3', b'l@cset-0-4')
725
716
def test_symlink_bundle(self):
726
717
self._test_symlink_bundle('link', 'bar/foo', 'mars')
728
719
def test_unicode_symlink_bundle(self):
729
self.requireFeature(tests.UnicodeFilenameFeature)
720
self.requireFeature(features.UnicodeFilenameFeature)
730
721
self._test_symlink_bundle(u'\N{Euro Sign}link',
731
722
u'bar/\N{Euro Sign}foo',
732
723
u'mars\N{Euro Sign}')
734
725
def test_binary_bundle(self):
735
726
self.tree1 = self.make_branch_and_tree('b1')
736
727
self.b1 = self.tree1.branch
737
tt = TreeTransform(self.tree1)
728
tt = self.tree1.get_transform()
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',
731
tt.new_file('file', tt.root, [
732
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
733
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')
736
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
737
self.get_valid_bundle(b'null:', b'b@cset-0-1')
748
tt = TreeTransform(self.tree1)
749
trans_id = tt.trans_id_tree_file_id('binary-1')
740
tt = self.tree1.get_transform()
741
trans_id = tt.trans_id_tree_path('file')
750
742
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')
744
self.tree1.commit('delete binary', rev_id=b'b@cset-0-2')
745
self.get_valid_bundle(b'b@cset-0-1', b'b@cset-0-2')
755
747
# Rename & modify
756
tt = TreeTransform(self.tree1)
757
trans_id = tt.trans_id_tree_file_id('binary-2')
748
tt = self.tree1.get_transform()
749
trans_id = tt.trans_id_tree_path('file2')
758
750
tt.adjust_path('file3', tt.root, trans_id)
759
751
tt.delete_contents(trans_id)
760
tt.create_file('file\rcontents\x00\n\x00', trans_id)
752
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')
754
self.tree1.commit('rename and modify binary', rev_id=b'b@cset-0-3')
755
self.get_valid_bundle(b'b@cset-0-2', b'b@cset-0-3')
766
tt = TreeTransform(self.tree1)
767
trans_id = tt.trans_id_tree_file_id('binary-2')
758
tt = self.tree1.get_transform()
759
trans_id = tt.trans_id_tree_path('file3')
768
760
tt.delete_contents(trans_id)
769
tt.create_file('\x00file\rcontents', trans_id)
761
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')
763
self.tree1.commit('just modify binary', rev_id=b'b@cset-0-4')
764
self.get_valid_bundle(b'b@cset-0-3', b'b@cset-0-4')
775
self.get_valid_bundle('null:', 'b@cset-0-4')
767
self.get_valid_bundle(b'null:', b'b@cset-0-4')
777
769
def test_last_modified(self):
778
770
self.tree1 = self.make_branch_and_tree('b1')
779
771
self.b1 = self.tree1.branch
780
tt = TreeTransform(self.tree1)
781
tt.new_file('file', tt.root, 'file', 'file')
783
self.tree1.commit('create file', rev_id='a@lmod-0-1')
785
tt = TreeTransform(self.tree1)
786
trans_id = tt.trans_id_tree_file_id('file')
787
tt.delete_contents(trans_id)
788
tt.create_file('file2', trans_id)
790
self.tree1.commit('modify text', rev_id='a@lmod-0-2a')
792
other = self.get_checkout('a@lmod-0-1')
793
tt = TreeTransform(other)
794
trans_id = tt.trans_id_tree_file_id('file')
795
tt.delete_contents(trans_id)
796
tt.create_file('file2', trans_id)
798
other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
772
tt = self.tree1.get_transform()
773
tt.new_file('file', tt.root, [b'file'], b'file')
775
self.tree1.commit('create file', rev_id=b'a@lmod-0-1')
777
tt = self.tree1.get_transform()
778
trans_id = tt.trans_id_tree_path('file')
779
tt.delete_contents(trans_id)
780
tt.create_file([b'file2'], trans_id)
782
self.tree1.commit('modify text', rev_id=b'a@lmod-0-2a')
784
other = self.get_checkout(b'a@lmod-0-1')
785
tt = other.get_transform()
786
trans_id = tt.trans_id_tree_path('file2')
787
tt.delete_contents(trans_id)
788
tt.create_file([b'file2'], trans_id)
790
other.commit('modify text in another tree', rev_id=b'a@lmod-0-2b')
799
791
self.tree1.merge_from_branch(other.branch)
800
self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
792
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')
794
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-4')
795
bundle = self.get_valid_bundle(b'a@lmod-0-2a', b'a@lmod-0-4')
805
797
def test_hide_history(self):
806
798
self.tree1 = self.make_branch_and_tree('b1')
807
799
self.b1 = self.tree1.branch
809
open('b1/one', 'wb').write('one\n')
801
with open('b1/one', 'wb') as f:
810
803
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')
804
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
805
with open('b1/one', 'wb') as f:
807
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
808
with open('b1/one', 'wb') as f:
810
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
811
bundle_file = BytesIO()
812
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
813
b'a@cset-0-1', bundle_file, format=self.format)
814
self.assertNotContainsRe(bundle_file.getvalue(), b'\btwo\b')
815
self.assertContainsRe(self.get_raw(bundle_file), b'one')
816
self.assertContainsRe(self.get_raw(bundle_file), b'three')
823
818
def test_bundle_same_basis(self):
824
819
"""Ensure using the basis as the target doesn't cause an error"""
825
820
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)
821
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
822
bundle_file = BytesIO()
823
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-1',
824
b'a@cset-0-1', bundle_file)
832
827
def get_raw(bundle_file):
833
828
return bundle_file.getvalue()
835
830
def test_unicode_bundle(self):
836
self.requireFeature(tests.UnicodeFilenameFeature)
831
self.requireFeature(features.UnicodeFilenameFeature)
837
832
# Handle international characters
839
834
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
842
837
self.b1 = self.tree1.branch
844
839
f.write((u'A file\n'
845
u'With international man of mystery\n'
846
u'William Dod\xe9\n').encode('utf-8'))
840
u'With international man of mystery\n'
841
u'William Dod\xe9\n').encode('utf-8'))
849
self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
844
self.tree1.add([u'with Dod\N{Euro Sign}'], [b'withdod-id'])
850
845
self.tree1.commit(u'i18n commit from William Dod\xe9',
851
rev_id='i18n-1', committer=u'William Dod\xe9')
846
rev_id=b'i18n-1', committer=u'William Dod\xe9')
854
bundle = self.get_valid_bundle('null:', 'i18n-1')
849
bundle = self.get_valid_bundle(b'null:', b'i18n-1')
857
852
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
858
853
f.write(u'Modified \xb5\n'.encode('utf8'))
860
self.tree1.commit(u'modified', rev_id='i18n-2')
855
self.tree1.commit(u'modified', rev_id=b'i18n-2')
862
bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
857
bundle = self.get_valid_bundle(b'i18n-1', b'i18n-2')
865
860
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',
861
self.tree1.commit(u'renamed, the new i18n man', rev_id=b'i18n-3',
867
862
committer=u'Erik B\xe5gfors')
869
bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
864
bundle = self.get_valid_bundle(b'i18n-2', b'i18n-3')
872
867
self.tree1.remove([u'B\N{Euro Sign}gfors'])
873
self.tree1.commit(u'removed', rev_id='i18n-4')
868
self.tree1.commit(u'removed', rev_id=b'i18n-4')
875
bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
870
bundle = self.get_valid_bundle(b'i18n-3', b'i18n-4')
878
bundle = self.get_valid_bundle('null:', 'i18n-4')
873
bundle = self.get_valid_bundle(b'null:', b'i18n-4')
881
875
def test_whitespace_bundle(self):
882
876
if sys.platform in ('win32', 'cygwin'):
941
936
def test_bundle_root_id(self):
942
937
self.tree1 = self.make_branch_and_tree('b1')
943
938
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)
939
self.tree1.commit('message', rev_id=b'revid1')
940
bundle = self.get_valid_bundle(b'null:', b'revid1')
941
tree = self.get_bundle_tree(bundle, b'revid1')
942
root_revision = tree.get_file_revision(u'')
943
self.assertEqual(b'revid1', root_revision)
949
945
def test_install_revisions(self):
950
946
self.tree1 = self.make_branch_and_tree('b1')
951
947
self.b1 = self.tree1.branch
952
self.tree1.commit('message', rev_id='rev2a')
953
bundle = self.get_valid_bundle('null:', 'rev2a')
948
self.tree1.commit('message', rev_id=b'rev2a')
949
bundle = self.get_valid_bundle(b'null:', b'rev2a')
954
950
branch2 = self.make_branch('b2')
955
self.assertFalse(branch2.repository.has_revision('rev2a'))
951
self.assertFalse(branch2.repository.has_revision(b'rev2a'))
956
952
target_revision = bundle.install_revisions(branch2.repository)
957
self.assertTrue(branch2.repository.has_revision('rev2a'))
958
self.assertEqual('rev2a', target_revision)
953
self.assertTrue(branch2.repository.has_revision(b'rev2a'))
954
self.assertEqual(b'rev2a', target_revision)
960
956
def test_bundle_empty_property(self):
961
957
"""Test serializing revision properties with an empty value."""
962
958
tree = self.make_branch_and_memory_tree('tree')
963
959
tree.lock_write()
964
960
self.addCleanup(tree.unlock)
965
tree.add([''], ['TREE_ROOT'])
966
tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
961
tree.add([''], [b'TREE_ROOT'])
962
tree.commit('One', revprops={u'one': 'two',
963
u'empty': ''}, rev_id=b'rev1')
967
964
self.b1 = tree.branch
968
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
965
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
969
966
bundle = read_bundle(bundle_sio)
970
967
revision_info = bundle.revisions[0]
971
self.assertEqual('rev1', revision_info.revision_id)
968
self.assertEqual(b'rev1', revision_info.revision_id)
972
969
rev = revision_info.as_revision()
973
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
970
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
976
973
def test_bundle_sorted_properties(self):
997
994
tree.lock_write()
998
995
self.addCleanup(tree.unlock)
1000
tree.add([''], ['TREE_ROOT'])
997
tree.add([''], [b'TREE_ROOT'])
1001
998
# Revisions themselves do not require anything about revision property
1002
999
# keys, other than that they are a basestring, and do not contain
1004
1001
# 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'})
1003
tree.commit('One', rev_id=b'rev1',
1004
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1008
1005
self.b1 = tree.branch
1009
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
1006
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1010
1007
bundle = read_bundle(bundle_sio)
1011
1008
revision_info = bundle.revisions[0]
1012
self.assertEqual('rev1', revision_info.revision_id)
1009
self.assertEqual(b'rev1', revision_info.revision_id)
1013
1010
rev = revision_info.as_revision()
1014
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1015
'alpha':u'\u03b1'}, rev.properties)
1011
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1012
'alpha': u'\u03b1'}, rev.properties)
1017
1014
def test_bundle_with_ghosts(self):
1018
1015
tree = self.make_branch_and_tree('tree')
1019
1016
self.b1 = tree.branch
1020
self.build_tree_contents([('tree/file', 'content1')])
1017
self.build_tree_contents([('tree/file', b'content1')])
1021
1018
tree.add(['file'])
1022
1019
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')
1020
self.build_tree_contents([('tree/file', b'content2')])
1021
tree.add_parent_tree_id(b'ghost')
1022
tree.commit('rev2', rev_id=b'rev2')
1023
bundle = self.get_valid_bundle(b'null:', b'rev2')
1028
1025
def make_simple_tree(self, format=None):
1029
1026
tree = self.make_branch_and_tree('b1', format=format)
1035
1032
def test_across_serializers(self):
1036
1033
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])
1034
tree.commit('hello', rev_id=b'rev1')
1035
tree.commit('hello', rev_id=b'rev2')
1036
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1040
1037
repo = self.make_repository('repo', format='dirstate-with-subtree')
1041
1038
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"')
1039
inv_text = b''.join(repo._get_inventory_xml(b'rev2'))
1040
self.assertNotContainsRe(inv_text, b'format="5"')
1041
self.assertContainsRe(inv_text, b'format="7"')
1046
1043
def make_repo_with_installed_revisions(self):
1047
1044
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])
1045
tree.commit('hello', rev_id=b'rev1')
1046
tree.commit('hello', rev_id=b'rev2')
1047
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1051
1048
repo = self.make_repository('repo', format='dirstate-with-subtree')
1052
1049
bundle.install_revisions(repo)
1055
1052
def test_across_models(self):
1056
1053
repo = self.make_repo_with_installed_revisions()
1057
inv = repo.get_inventory('rev2')
1058
self.assertEqual('rev2', inv.root.revision)
1054
inv = repo.get_inventory(b'rev2')
1055
self.assertEqual(b'rev2', inv.root.revision)
1059
1056
root_id = inv.root.file_id
1060
1057
repo.lock_read()
1061
1058
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')]))
1059
self.assertEqual({(root_id, b'rev1'): (),
1060
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1061
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1066
1063
def test_inv_hash_across_serializers(self):
1067
1064
repo = self.make_repo_with_installed_revisions()
1068
recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1069
xml = repo._get_inventory_xml('rev2')
1065
recorded_inv_sha1 = repo.get_revision(b'rev2').inventory_sha1
1066
xml = b''.join(repo._get_inventory_xml(b'rev2'))
1070
1067
self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1072
1069
def test_across_models_incompatible(self):
1073
1070
tree = self.make_simple_tree('dirstate-with-subtree')
1074
tree.commit('hello', rev_id='rev1')
1075
tree.commit('hello', rev_id='rev2')
1071
tree.commit('hello', rev_id=b'rev1')
1072
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1074
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1078
1075
except errors.IncompatibleBundleFormat:
1079
1076
raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1080
1077
repo = self.make_repository('repo', format='knit')
1081
1078
bundle.install_revisions(repo)
1083
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1080
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1084
1081
self.assertRaises(errors.IncompatibleRevision,
1085
1082
bundle.install_revisions, repo)
1087
1084
def test_get_merge_request(self):
1088
1085
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])
1086
tree.commit('hello', rev_id=b'rev1')
1087
tree.commit('hello', rev_id=b'rev2')
1088
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1092
1089
result = bundle.get_merge_request(tree.branch.repository)
1093
self.assertEqual((None, 'rev1', 'inapplicable'), result)
1090
self.assertEqual((None, b'rev1', 'inapplicable'), result)
1095
1092
def test_with_subtree(self):
1096
1093
tree = self.make_branch_and_tree('tree',
1116
1113
self.tree1 = self.make_branch_and_tree('tree')
1117
1114
self.b1 = self.tree1.branch
1119
self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1116
self.tree1.commit('Revision/id/with/slashes', rev_id=b'rev/id')
1120
1117
except ValueError:
1121
1118
raise tests.TestSkipped(
1122
1119
"Repository doesn't support revision ids with slashes")
1123
bundle = self.get_valid_bundle('null:', 'rev/id')
1120
bundle = self.get_valid_bundle(b'null:', b'rev/id')
1125
1122
def test_skip_file(self):
1126
1123
"""Make sure we don't accidentally write to the wrong versionedfile"""
1127
1124
self.tree1 = self.make_branch_and_tree('tree')
1128
1125
self.b1 = self.tree1.branch
1129
1126
# 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')])
1127
self.build_tree_contents([('tree/file2', b'contents1')])
1128
self.tree1.add('file2', b'file2-id')
1129
self.tree1.commit('rev1', rev_id=b'reva')
1130
self.build_tree_contents([('tree/file3', b'contents2')])
1134
1131
# rev2 is present in bundle, and done by fetch
1135
1132
# having file1 in the bunle causes file1's versionedfile to be opened.
1136
self.tree1.add('file3', 'file3-id')
1137
self.tree1.commit('rev2')
1133
self.tree1.add('file3', b'file3-id')
1134
rev2 = self.tree1.commit('rev2')
1138
1135
# 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')
1136
target = self.tree1.controldir.sprout('target').open_workingtree()
1137
self.build_tree_contents([('tree/file2', b'contents3')])
1138
self.tree1.commit('rev3', rev_id=b'rev3')
1139
bundle = self.get_valid_bundle(b'reva', b'rev3')
1143
1140
if getattr(bundle, 'get_bundle_reader', None) is None:
1144
1141
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')
1143
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1146
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1150
1147
bundle.install_revisions(target.branch.repository)
1402
1403
line = bundle_file.readline()
1403
1404
line = bundle_file.readline()
1404
1405
lines = bundle_file.readlines()
1405
return ''.join(lines).decode('bz2')
1406
return bz2.decompress(b''.join(lines))
1407
1408
def test_copy_signatures(self):
1408
1409
tree_a = self.make_branch_and_tree('tree_a')
1410
import bzrlib.commit as commit
1411
oldstrategy = bzrlib.gpg.GPGStrategy
1411
import breezy.commit as commit
1412
oldstrategy = breezy.gpg.GPGStrategy
1412
1413
branch = tree_a.branch
1413
1414
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'))
1415
tree_a.commit("base", allow_pointless=True, rev_id=b'A')
1416
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
1417
from bzrlib.testament import Testament
1418
from ..bzr.testament import Testament
1418
1419
# 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)
1420
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
1421
new_config = test_commit.MustSignConfig()
1422
commit.Commit(config_stack=new_config).commit(message="base",
1423
allow_pointless=True,
1425
working_tree=tree_a)
1425
1427
def sign(text):
1426
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
1427
self.assertTrue(repo_a.has_signature_for_revision_id('B'))
1428
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
1429
self.assertTrue(repo_a.has_signature_for_revision_id(b'B'))
1429
bzrlib.gpg.GPGStrategy = oldstrategy
1431
breezy.gpg.GPGStrategy = oldstrategy
1430
1432
tree_b = self.make_branch_and_tree('tree_b')
1431
1433
repo_b = tree_b.branch.repository
1433
1435
serializer = BundleSerializerV4('4')
1434
serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
1436
with tree_a.lock_read():
1437
serializer.write_bundle(
1438
tree_a.branch.repository, b'B', b'null:', s)
1436
1440
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'))
1441
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1442
self.assertEqual(repo_b.get_signature_text(b'B'),
1443
repo_a.get_signature_text(b'B'))
1441
1445
# ensure repeat installs are harmless
1442
1446
install_bundle(repo_b, serializer.read(s))
1445
class V4WeaveBundleTester(V4BundleTester):
1447
def bzrdir_format(self):
1451
1449
class V4_2aBundleTester(V4BundleTester):
1453
1451
def bzrdir_format(self):
1480
1478
def make_merged_branch(self):
1481
1479
builder = self.make_branch_builder('source')
1482
1480
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')),
1481
builder.build_snapshot(None, [
1482
('add', ('', b'root-id', 'directory', None)),
1483
('add', ('file', b'file-id', 'file', b'original content\n')),
1484
], revision_id=b'a@cset-0-1')
1485
builder.build_snapshot([b'a@cset-0-1'], [
1486
('modify', ('file', b'new-content\n')),
1487
], revision_id=b'a@cset-0-2a')
1488
builder.build_snapshot([b'a@cset-0-1'], [
1489
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1490
], revision_id=b'a@cset-0-2b')
1491
builder.build_snapshot([b'a@cset-0-2a', b'a@cset-0-2b'], [
1492
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1493
], revision_id=b'a@cset-0-3')
1496
1494
builder.finish_series()
1497
1495
self.b1 = builder.get_branch()
1498
1496
self.b1.lock_read()
1513
1511
def test_single_inventory_multiple_parents_as_xml(self):
1514
1512
self.make_merged_branch()
1515
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1513
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1517
1515
reader = v4.BundleReader(sio, stream_input=False)
1518
1516
records = list(reader.iter_records())
1519
1517
self.assertEqual(1, len(records))
1520
1518
(bytes, metadata, repo_kind, revision_id,
1521
1519
file_id) = records[0]
1522
1520
self.assertIs(None, file_id)
1523
self.assertEqual('a@cset-0-3', revision_id)
1521
self.assertEqual(b'a@cset-0-3', revision_id)
1524
1522
self.assertEqual('inventory', repo_kind)
1525
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
'storage_kind': 'mpdiff',
1523
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1524
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1525
b'storage_kind': b'mpdiff',
1529
1527
# We should have an mpdiff that takes some lines from both parents.
1530
1528
self.assertEqualDiff(
1532
'<inventory format="10" revision_id="a@cset-0-3">\n'
1535
'c 1 3 3 2\n', bytes)
1530
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1533
b'c 1 3 3 2\n', bytes)
1537
1535
def test_single_inv_no_parents_as_xml(self):
1538
1536
self.make_merged_branch()
1539
sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1537
sio = self.make_bundle_just_inventories(b'null:', b'a@cset-0-1',
1541
1539
reader = v4.BundleReader(sio, stream_input=False)
1542
1540
records = list(reader.iter_records())
1543
1541
self.assertEqual(1, len(records))
1544
1542
(bytes, metadata, repo_kind, revision_id,
1545
1543
file_id) = records[0]
1546
1544
self.assertIs(None, file_id)
1547
self.assertEqual('a@cset-0-1', revision_id)
1545
self.assertEqual(b'a@cset-0-1', revision_id)
1548
1546
self.assertEqual('inventory', repo_kind)
1549
self.assertEqual({'parents': [],
1550
'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
'storage_kind': 'mpdiff',
1547
self.assertEqual({b'parents': [],
1548
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1549
b'storage_kind': b'mpdiff',
1553
1551
# We should have an mpdiff that takes some lines from both parents.
1554
1552
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'
1554
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1555
b'<directory file_id="root-id" name=""'
1556
b' revision="a@cset-0-1" />\n'
1557
b'<file file_id="file-id" name="file" parent_id="root-id"'
1558
b' revision="a@cset-0-1"'
1559
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1560
b' text_size="17" />\n'
1566
1564
def test_multiple_inventories_as_xml(self):
1567
1565
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'])
1566
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1567
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1570
1568
reader = v4.BundleReader(sio, stream_input=False)
1571
1569
records = list(reader.iter_records())
1572
1570
self.assertEqual(3, len(records))
1573
1571
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'],
1572
self.assertEqual([b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'],
1576
1574
metadata_2a = records[0][1]
1577
self.assertEqual({'parents': ['a@cset-0-1'],
1578
'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
'storage_kind': 'mpdiff',
1575
self.assertEqual({b'parents': [b'a@cset-0-1'],
1576
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1577
b'storage_kind': b'mpdiff',
1581
1579
metadata_2b = records[1][1]
1582
self.assertEqual({'parents': ['a@cset-0-1'],
1583
'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
'storage_kind': 'mpdiff',
1580
self.assertEqual({b'parents': [b'a@cset-0-1'],
1581
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1582
b'storage_kind': b'mpdiff',
1586
1584
metadata_3 = records[2][1]
1587
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
'storage_kind': 'mpdiff',
1585
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1586
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1587
b'storage_kind': b'mpdiff',
1591
1589
bytes_2a = records[0][0]
1592
1590
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)
1592
b'<inventory format="10" revision_id="a@cset-0-2a">\n'
1596
b'<file file_id="file-id" name="file" parent_id="root-id"'
1597
b' revision="a@cset-0-2a"'
1598
b' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1599
b' text_size="12" />\n'
1601
b'c 0 3 3 1\n', bytes_2a)
1604
1602
bytes_2b = records[1][0]
1605
1603
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)
1605
b'<inventory format="10" revision_id="a@cset-0-2b">\n'
1609
b'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1610
b' revision="a@cset-0-2b"'
1611
b' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1612
b' text_size="14" />\n'
1614
b'c 0 3 4 1\n', bytes_2b)
1617
1615
bytes_3 = records[2][0]
1618
1616
self.assertEqualDiff(
1620
'<inventory format="10" revision_id="a@cset-0-3">\n'
1623
'c 1 3 3 2\n', bytes_3)
1618
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1621
b'c 1 3 3 2\n', bytes_3)
1625
1623
def test_creating_bundle_preserves_chk_pages(self):
1626
1624
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)
1625
target = self.b1.controldir.sprout('target',
1626
revision_id=b'a@cset-0-2a').open_branch()
1627
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1629
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1632
1630
bundle = read_bundle(bundle_txt)
1633
1631
target.lock_write()
1634
1632
self.addCleanup(target.unlock)
1635
1633
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')
1634
inv1 = next(self.b1.repository.inventories.get_record_stream([
1635
(b'a@cset-0-3',)], 'unordered',
1636
True)).get_bytes_as('fulltext')
1637
inv2 = next(target.repository.inventories.get_record_stream([
1638
(b'a@cset-0-3',)], 'unordered',
1639
True)).get_bytes_as('fulltext')
1642
1640
self.assertEqualDiff(inv1, inv2)
1740
1738
class TestBundleWriterReader(tests.TestCase):
1742
1740
def test_roundtrip_record(self):
1743
fileobj = StringIO()
1744
1742
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')
1744
writer.add_info_record({b'foo': b'bar'})
1745
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1746
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1750
1748
fileobj.seek(0)
1751
1749
reader = v4.BundleReader(fileobj, stream_input=True)
1752
1750
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'),
1751
record = next(record_iter)
1752
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1753
'info', None, None), record)
1754
record = next(record_iter)
1755
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1756
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1761
1759
def test_roundtrip_record_memory_hungry(self):
1762
fileobj = StringIO()
1763
1761
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')
1763
writer.add_info_record({b'foo': b'bar'})
1764
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1765
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1769
1767
fileobj.seek(0)
1770
1768
reader = v4.BundleReader(fileobj, stream_input=False)
1771
1769
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'),
1770
record = next(record_iter)
1771
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1772
'info', None, None), record)
1773
record = next(record_iter)
1774
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1775
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1780
1778
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))
1779
self.assertEqual(b'revision/rev1',
1780
v4.BundleWriter.encode_name('revision', b'rev1'))
1781
self.assertEqual(b'file/rev//1/file-id-1',
1782
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1783
self.assertEqual(b'info',
1784
v4.BundleWriter.encode_name('info', None, None))
1788
1786
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'))
1787
self.assertEqual(('revision', b'rev1', None),
1788
v4.BundleReader.decode_name(b'revision/rev1'))
1789
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1790
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1793
1791
self.assertEqual(('info', None, None),
1794
v4.BundleReader.decode_name('info'))
1792
v4.BundleReader.decode_name(b'info'))
1796
1794
def test_too_many_names(self):
1797
fileobj = StringIO()
1798
1796
writer = v4.BundleWriter(fileobj)
1800
writer.add_info_record(foo='bar')
1801
writer._container.add_bytes_record('blah', ['two', 'names'])
1798
writer.add_info_record({b'foo': b'bar'})
1799
writer._container.add_bytes_record([b'blah'], len(b'blah'), [(b'two', ), (b'names', )])
1803
1801
fileobj.seek(0)
1804
1802
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:
1803
record = next(record_iter)
1804
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1805
'info', None, None), record)
1806
self.assertRaises(errors.BadBundle, next, record_iter)