15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from cStringIO import StringIO
18
from io import BytesIO
27
from bzrlib.branch import Branch
28
from bzrlib.bzrdir import BzrDir
29
from bzrlib.lockdir import LockDir
30
from bzrlib.mutabletree import needs_tree_write_lock
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
from bzrlib.transport import get_transport
33
from bzrlib.workingtree import (
30
workingtree as bzrworkingtree,
34
from ..lock import write_locked
35
from ..lockdir import LockDir
36
from . import TestCase, TestCaseWithTransport, TestSkipped
44
from .features import SymlinkFeature
41
46
class TestTreeDirectory(TestCaseWithTransport):
65
70
class TestDefaultFormat(TestCaseWithTransport):
67
72
def test_get_set_default_format(self):
68
old_format = workingtree.WorkingTreeFormat.get_default_format()
70
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
71
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
73
# the default branch format is used by the meta dir format
74
# which is not the default bzrdir format at this point
75
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
76
dir.create_repository()
78
result = dir.create_workingtree()
79
self.assertEqual(result, 'A tree')
81
workingtree.WorkingTreeFormat.set_default_format(old_format)
82
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
73
old_format = workingtree.format_registry.get_default()
75
self.assertTrue(isinstance(
76
old_format, workingtree_4.WorkingTreeFormat6))
77
workingtree.format_registry.set_default(SampleTreeFormat())
79
# the default branch format is used by the meta dir format
80
# which is not the default bzrdir format at this point
81
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
82
dir.create_repository()
84
result = dir.create_workingtree()
85
self.assertEqual(result, 'A tree')
87
workingtree.format_registry.set_default(old_format)
88
self.assertEqual(old_format, workingtree.format_registry.get_default())
90
def test_from_string(self):
91
self.assertIsInstance(
92
SampleTreeFormat.from_string(b"Sample tree format."),
95
AssertionError, SampleTreeFormat.from_string,
96
b"Different format string.")
98
def test_get_set_default_format_by_key(self):
99
old_format = workingtree.format_registry.get_default()
101
format = SampleTreeFormat()
102
workingtree.format_registry.register(format)
103
self.addCleanup(workingtree.format_registry.remove, format)
104
self.assertTrue(isinstance(
105
old_format, workingtree_4.WorkingTreeFormat6))
106
workingtree.format_registry.set_default_key(format.get_format_string())
108
# the default branch format is used by the meta dir format
109
# which is not the default bzrdir format at this point
110
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
111
dir.create_repository()
113
result = dir.create_workingtree()
114
self.assertEqual(result, 'A tree')
116
workingtree.format_registry.set_default_key(
117
old_format.get_format_string())
118
self.assertEqual(old_format, workingtree.format_registry.get_default())
84
120
def test_open(self):
85
121
tree = self.make_branch_and_tree('.')
96
132
open_no_args, relpath = workingtree.WorkingTree.open_containing()
97
133
self.assertEqual(tree.basedir, open_no_args.basedir)
98
134
self.assertEqual('', relpath)
99
open_subdir, relpath = workingtree.WorkingTree.open_containing('subdir')
135
open_subdir, relpath = workingtree.WorkingTree.open_containing(
100
137
self.assertEqual(tree.basedir, open_subdir.basedir)
101
138
self.assertEqual('subdir', relpath)
104
class SampleTreeFormat(workingtree.WorkingTreeFormat):
141
class SampleTreeFormat(bzrworkingtree.WorkingTreeFormatMetaDir):
105
142
"""A sample format
107
144
this format is initializable, unsupported to aid in testing the
108
145
open and open_downlevel routines.
111
def get_format_string(self):
149
def get_format_string(cls):
112
150
"""See WorkingTreeFormat.get_format_string()."""
113
return "Sample tree format."
151
return b"Sample tree format."
115
def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
153
def initialize(self, a_controldir, revision_id=None, from_branch=None,
116
154
accelerator_tree=None, hardlink=False):
117
155
"""Sample branches cannot be created."""
118
t = a_bzrdir.get_workingtree_transport(self)
156
t = a_controldir.get_workingtree_transport(self)
119
157
t.put_bytes('format', self.get_format_string())
126
164
return "opened tree."
167
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
168
"""A sample format that does not support use in a metadir.
172
def get_format_string(self):
173
# Not usable in a metadir, so no format string
176
def initialize(self, a_controldir, revision_id=None, from_branch=None,
177
accelerator_tree=None, hardlink=False):
178
raise NotImplementedError(self.initialize)
180
def is_supported(self):
183
def open(self, transport, _found=False):
184
raise NotImplementedError(self.open)
129
187
class TestWorkingTreeFormat(TestCaseWithTransport):
130
188
"""Tests for the WorkingTreeFormat facility."""
190
def test_find_format_string(self):
191
# is the right format object found for a working tree?
192
branch = self.make_branch('branch')
194
errors.NoWorkingTree,
195
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string,
197
transport = branch.controldir.get_workingtree_transport(None)
199
transport.put_bytes("format", b"some format name")
200
# The format does not have to be known by Bazaar,
201
# find_format_string just retrieves the name
204
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string(
132
207
def test_find_format(self):
133
208
# is the right format object found for a working tree?
134
209
# create a branch with a few known format objects.
135
210
self.build_tree(["foo/", "bar/"])
136
212
def check_format(format, url):
137
dir = format._matchingbzrdir.initialize(url)
213
dir = format._matchingcontroldir.initialize(url)
138
214
dir.create_repository()
139
215
dir.create_branch()
140
216
format.initialize(dir)
141
t = get_transport(url)
142
found_format = workingtree.WorkingTreeFormat.find_format(dir)
143
self.failUnless(isinstance(found_format, format.__class__))
144
check_format(workingtree.WorkingTreeFormat3(), "bar")
217
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
219
self.assertIsInstance(found_format, format.__class__)
220
check_format(workingtree_3.WorkingTreeFormat3(), "bar")
146
222
def test_find_format_no_tree(self):
147
223
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
148
224
self.assertRaises(errors.NoWorkingTree,
149
workingtree.WorkingTreeFormat.find_format,
225
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
152
228
def test_find_format_unknown_format(self):
155
231
dir.create_branch()
156
232
SampleTreeFormat().initialize(dir)
157
233
self.assertRaises(errors.UnknownFormatError,
158
workingtree.WorkingTreeFormat.find_format,
234
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
237
def test_find_format_with_features(self):
238
tree = self.make_branch_and_tree('.', format='2a')
239
tree.update_feature_flags({b"name": b"necessity"})
240
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
242
self.assertIsInstance(found_format, workingtree.WorkingTreeFormat)
243
self.assertEqual(found_format.features.get(b"name"), b"necessity")
245
bzrdir.MissingFeature, found_format.check_support_status, True)
247
bzrworkingtree.WorkingTreeFormatMetaDir.unregister_feature,
249
bzrworkingtree.WorkingTreeFormatMetaDir.register_feature(b"name")
250
found_format.check_support_status(True)
253
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
255
def make_simple_tree(self):
256
tree = self.make_branch_and_tree('tree', format='development-subtree')
257
self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
258
tree.set_root_id(b'root-id')
259
tree.add(['a', 'a/b', 'a/b/c'], [b'a-id', b'b-id', b'c-id'])
260
tree.commit('initial')
263
def test_just_directory(self):
264
tree = self.make_simple_tree()
265
self.assertEqual([('directory', b'root-id'),
266
('directory', b'a-id'),
267
('directory', b'b-id'),
269
[(ie.kind, ie.file_id)
270
for path, ie in tree.iter_entries_by_dir()])
271
self.make_branch_and_tree('tree/a/b')
272
self.assertEqual([('tree-reference', b'b-id')],
273
[(ie.kind, ie.file_id)
274
for path, ie in tree.iter_entries_by_dir(
275
specific_files=['a/b'])])
277
def test_direct_subtree(self):
278
tree = self.make_simple_tree()
279
self.make_branch_and_tree('tree/a/b')
280
self.assertEqual([('directory', b'root-id'),
281
('directory', b'a-id'),
282
('tree-reference', b'b-id')],
283
[(ie.kind, ie.file_id)
284
for path, ie in tree.iter_entries_by_dir()])
286
def test_indirect_subtree(self):
287
tree = self.make_simple_tree()
288
self.make_branch_and_tree('tree/a')
289
self.assertEqual([('directory', b'root-id'),
290
('tree-reference', b'a-id')],
291
[(ie.kind, ie.file_id)
292
for path, ie in tree.iter_entries_by_dir()])
295
class TestWorkingTreeFormatRegistry(TestCase):
298
super(TestWorkingTreeFormatRegistry, self).setUp()
299
self.registry = workingtree.WorkingTreeFormatRegistry()
161
301
def test_register_unregister_format(self):
162
302
format = SampleTreeFormat()
164
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
165
dir.create_repository()
168
format.initialize(dir)
169
# register a format for it.
170
workingtree.WorkingTreeFormat.register_format(format)
171
# which branch.Open will refuse (not supported)
172
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
173
# but open_downlevel will work
174
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
175
# unregister the format
176
workingtree.WorkingTreeFormat.unregister_format(format)
303
self.registry.register(format)
304
self.assertEqual(format, self.registry.get(b"Sample tree format."))
305
self.registry.remove(format)
306
self.assertRaises(KeyError, self.registry.get, b"Sample tree format.")
308
def test_get_all(self):
309
format = SampleTreeFormat()
310
self.assertEqual([], self.registry._get_all())
311
self.registry.register(format)
312
self.assertEqual([format], self.registry._get_all())
314
def test_register_extra(self):
315
format = SampleExtraTreeFormat()
316
self.assertEqual([], self.registry._get_all())
317
self.registry.register_extra(format)
318
self.assertEqual([format], self.registry._get_all())
320
def test_register_extra_lazy(self):
321
self.assertEqual([], self.registry._get_all())
322
self.registry.register_extra_lazy("breezy.tests.test_workingtree",
323
"SampleExtraTreeFormat")
324
formats = self.registry._get_all()
325
self.assertEqual(1, len(formats))
326
self.assertIsInstance(formats[0], SampleExtraTreeFormat)
179
329
class TestWorkingTreeFormat3(TestCaseWithTransport):
214
364
t = self.get_transport()
215
365
url = self.get_url()
216
366
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
217
repo = dir.create_repository()
218
branch = dir.create_branch()
367
dir.create_repository()
220
tree = workingtree.WorkingTreeFormat3().initialize(dir)
370
tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
221
371
except errors.NotLocalUrl:
222
372
raise TestSkipped('Not a local URL')
223
373
self.assertIsDirectory('.bzr', t)
224
374
self.assertIsDirectory('.bzr/checkout', t)
225
375
self.assertIsDirectory('.bzr/checkout/lock', t)
226
376
our_lock = LockDir(t, '.bzr/checkout/lock')
227
self.assertEquals(our_lock.peek(), None)
229
self.assertTrue(our_lock.peek())
231
self.assertEquals(our_lock.peek(), None)
377
self.assertEqual(our_lock.peek(), None)
378
with tree.lock_write():
379
self.assertTrue(our_lock.peek())
380
self.assertEqual(our_lock.peek(), None)
233
382
def test_missing_pending_merges(self):
234
383
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
235
384
control.create_repository()
236
385
control.create_branch()
237
tree = workingtree.WorkingTreeFormat3().initialize(control)
386
tree = workingtree_3.WorkingTreeFormat3().initialize(control)
238
387
tree._transport.delete("pending-merges")
239
388
self.assertEqual([], tree.get_parent_ids())
242
class TestFormat2WorkingTree(TestCaseWithTransport):
243
"""Tests that are specific to format 2 trees."""
245
def create_format2_tree(self, url):
246
return self.make_branch_and_tree(
247
url, format=bzrdir.BzrDirFormat6())
249
def test_conflicts(self):
250
# test backwards compatability
251
tree = self.create_format2_tree('.')
252
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
254
file('lala.BASE', 'wb').write('labase')
255
expected = conflicts.ContentsConflict('lala')
256
self.assertEqual(list(tree.conflicts()), [expected])
257
file('lala', 'wb').write('la')
258
tree.add('lala', 'lala-id')
259
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
260
self.assertEqual(list(tree.conflicts()), [expected])
261
file('lala.THIS', 'wb').write('lathis')
262
file('lala.OTHER', 'wb').write('laother')
263
# When "text conflict"s happen, stem, THIS and OTHER are text
264
expected = conflicts.TextConflict('lala', file_id='lala-id')
265
self.assertEqual(list(tree.conflicts()), [expected])
266
os.unlink('lala.OTHER')
267
os.mkdir('lala.OTHER')
268
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
269
self.assertEqual(list(tree.conflicts()), [expected])
272
class InstrumentedTree(object):
273
"""A instrumented tree to check the needs_tree_write_lock decorator."""
278
def lock_tree_write(self):
279
self._locks.append('t')
281
@needs_tree_write_lock
282
def method_with_tree_write_lock(self, *args, **kwargs):
283
"""A lock_tree_write decorated method that returns its arguments."""
286
@needs_tree_write_lock
287
def method_that_raises(self):
288
"""This method causes an exception when called with parameters.
290
This allows the decorator code to be checked - it should still call
295
self._locks.append('u')
298
class TestInstrumentedTree(TestCase):
300
def test_needs_tree_write_lock(self):
301
"""@needs_tree_write_lock should be semantically transparent."""
302
tree = InstrumentedTree()
304
'method_with_tree_write_lock',
305
tree.method_with_tree_write_lock.__name__)
306
self.assertDocstring(
307
"A lock_tree_write decorated method that returns its arguments.",
308
tree.method_with_tree_write_lock)
311
result = tree.method_with_tree_write_lock(1,2,3, a='b')
312
self.assertEqual((args, kwargs), result)
313
self.assertEqual(['t', 'u'], tree._locks)
314
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
315
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)
318
391
class TestRevert(TestCaseWithTransport):
320
393
def test_revert_conflicts_recursive(self):
321
394
this_tree = self.make_branch_and_tree('this-tree')
322
395
self.build_tree_contents([('this-tree/foo/',),
323
('this-tree/foo/bar', 'bar')])
396
('this-tree/foo/bar', b'bar')])
324
397
this_tree.add(['foo', 'foo/bar'])
325
398
this_tree.commit('created foo/bar')
326
other_tree = this_tree.bzrdir.sprout('other-tree').open_workingtree()
327
self.build_tree_contents([('other-tree/foo/bar', 'baz')])
399
other_tree = this_tree.controldir.sprout(
400
'other-tree').open_workingtree()
401
self.build_tree_contents([('other-tree/foo/bar', b'baz')])
328
402
other_tree.commit('changed bar')
329
self.build_tree_contents([('this-tree/foo/bar', 'qux')])
403
self.build_tree_contents([('this-tree/foo/bar', b'qux')])
330
404
this_tree.commit('changed qux')
331
405
this_tree.merge_from_branch(other_tree.branch)
332
406
self.assertEqual(1, len(this_tree.conflicts()))
339
413
def test_auto_resolve(self):
340
414
base = self.make_branch_and_tree('base')
341
self.build_tree_contents([('base/hello', 'Hello')])
342
base.add('hello', 'hello_id')
415
self.build_tree_contents([('base/hello', b'Hello')])
416
base.add('hello', b'hello_id')
343
417
base.commit('Hello')
344
other = base.bzrdir.sprout('other').open_workingtree()
345
self.build_tree_contents([('other/hello', 'hELLO')])
418
other = base.controldir.sprout('other').open_workingtree()
419
self.build_tree_contents([('other/hello', b'hELLO')])
346
420
other.commit('Case switch')
347
this = base.bzrdir.sprout('this').open_workingtree()
348
self.failUnlessExists('this/hello')
349
self.build_tree_contents([('this/hello', 'Hello World')])
421
this = base.controldir.sprout('this').open_workingtree()
422
self.assertPathExists('this/hello')
423
self.build_tree_contents([('this/hello', b'Hello World')])
350
424
this.commit('Add World')
351
425
this.merge_from_branch(other.branch)
352
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
355
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
357
self.build_tree_contents([('this/hello', '<<<<<<<')])
359
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
361
self.build_tree_contents([('this/hello', '=======')])
363
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
365
self.build_tree_contents([('this/hello', '\n>>>>>>>')])
426
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
429
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
431
self.build_tree_contents([('this/hello', b'<<<<<<<')])
433
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
435
self.build_tree_contents([('this/hello', b'=======')])
437
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
439
self.build_tree_contents([('this/hello', b'\n>>>>>>>')])
366
440
remaining, resolved = this.auto_resolve()
367
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
441
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
368
442
this.conflicts())
369
443
self.assertEqual([], resolved)
370
self.build_tree_contents([('this/hello', 'hELLO wORLD')])
444
self.build_tree_contents([('this/hello', b'hELLO wORLD')])
371
445
remaining, resolved = this.auto_resolve()
372
446
self.assertEqual([], this.conflicts())
373
self.assertEqual([conflicts.TextConflict('hello', None, 'hello_id')],
447
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
375
self.failIfExists('this/hello.BASE')
449
self.assertPathDoesNotExist('this/hello.BASE')
451
def test_unsupported_symlink_auto_resolve(self):
452
self.requireFeature(SymlinkFeature)
453
base = self.make_branch_and_tree('base')
454
self.build_tree_contents([('base/hello', 'Hello')])
455
base.add('hello', 'hello_id')
456
base.commit('commit 0')
457
other = base.controldir.sprout('other').open_workingtree()
458
self.build_tree_contents([('other/hello', 'Hello')])
459
os.symlink('other/hello', 'other/foo')
460
other.add('foo', 'foo_id')
461
other.commit('commit symlink')
462
this = base.controldir.sprout('this').open_workingtree()
463
self.assertPathExists('this/hello')
464
self.build_tree_contents([('this/hello', 'Hello')])
465
this.commit('commit 2')
467
trace.push_log_file(log)
468
os_symlink = getattr(os, 'symlink', None)
471
this.merge_from_branch(other.branch)
474
os.symlink = os_symlink
475
self.assertContainsRe(
477
b'Unable to create symlink "foo" on this filesystem')
377
479
def test_auto_resolve_dir(self):
378
480
tree = self.make_branch_and_tree('tree')
379
481
self.build_tree(['tree/hello/'])
380
tree.add('hello', 'hello-id')
381
file_conflict = conflicts.TextConflict('file', None, 'hello-id')
482
tree.add('hello', b'hello-id')
483
file_conflict = conflicts.TextConflict('file', b'hello-id')
382
484
tree.set_conflicts(conflicts.ConflictList([file_conflict]))
383
485
tree.auto_resolve()
393
495
self.make_branch('qux')
394
496
trees = workingtree.WorkingTree.find_trees('.')
395
497
self.assertEqual(2, len(list(trees)))
500
class TestStoredUncommitted(TestCaseWithTransport):
502
def store_uncommitted(self):
503
tree = self.make_branch_and_tree('tree')
504
tree.commit('get root in there')
505
self.build_tree_contents([('tree/file', b'content')])
506
tree.add('file', b'file-id')
507
tree.store_uncommitted()
510
def test_store_uncommitted(self):
511
self.store_uncommitted()
512
self.assertPathDoesNotExist('tree/file')
514
def test_store_uncommitted_no_change(self):
515
tree = self.make_branch_and_tree('tree')
516
tree.commit('get root in there')
517
tree.store_uncommitted()
518
self.assertIs(None, tree.branch.get_unshelver(tree))
520
def test_restore_uncommitted(self):
521
with write_locked(self.store_uncommitted()) as tree:
522
tree.restore_uncommitted()
523
self.assertPathExists('tree/file')
524
self.assertIs(None, tree.branch.get_unshelver(tree))
526
def test_restore_uncommitted_none(self):
527
tree = self.make_branch_and_tree('tree')
528
tree.restore_uncommitted()