1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
# Authors: Robert Collins <robert.collins@canonical.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from io import BytesIO
31
conflicts as _mod_bzr_conflicts,
32
workingtree as bzrworkingtree,
36
from ..lock import write_locked
37
from ..lockdir import LockDir
38
from . import TestCase, TestCaseWithTransport, TestSkipped
46
from .features import SymlinkFeature
48
class TestTreeDirectory(TestCaseWithTransport):
50
def test_kind_character(self):
51
self.assertEqual(TreeDirectory().kind_character(), '/')
54
class TestTreeEntry(TestCaseWithTransport):
56
def test_kind_character(self):
57
self.assertEqual(TreeEntry().kind_character(), '???')
60
class TestTreeFile(TestCaseWithTransport):
62
def test_kind_character(self):
63
self.assertEqual(TreeFile().kind_character(), '')
66
class TestTreeLink(TestCaseWithTransport):
68
def test_kind_character(self):
69
self.assertEqual(TreeLink().kind_character(), '')
72
class TestDefaultFormat(TestCaseWithTransport):
74
def test_get_set_default_format(self):
75
old_format = workingtree.format_registry.get_default()
77
self.assertTrue(isinstance(
78
old_format, workingtree_4.WorkingTreeFormat6))
79
workingtree.format_registry.set_default(SampleTreeFormat())
81
# the default branch format is used by the meta dir format
82
# which is not the default bzrdir format at this point
83
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
84
dir.create_repository()
86
result = dir.create_workingtree()
87
self.assertEqual(result, 'A tree')
89
workingtree.format_registry.set_default(old_format)
90
self.assertEqual(old_format, workingtree.format_registry.get_default())
92
def test_from_string(self):
93
self.assertIsInstance(
94
SampleTreeFormat.from_string(b"Sample tree format."),
97
AssertionError, SampleTreeFormat.from_string,
98
b"Different format string.")
100
def test_get_set_default_format_by_key(self):
101
old_format = workingtree.format_registry.get_default()
103
format = SampleTreeFormat()
104
workingtree.format_registry.register(format)
105
self.addCleanup(workingtree.format_registry.remove, format)
106
self.assertTrue(isinstance(
107
old_format, workingtree_4.WorkingTreeFormat6))
108
workingtree.format_registry.set_default_key(format.get_format_string())
110
# the default branch format is used by the meta dir format
111
# which is not the default bzrdir format at this point
112
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
113
dir.create_repository()
115
result = dir.create_workingtree()
116
self.assertEqual(result, 'A tree')
118
workingtree.format_registry.set_default_key(
119
old_format.get_format_string())
120
self.assertEqual(old_format, workingtree.format_registry.get_default())
123
tree = self.make_branch_and_tree('.')
124
open_direct = workingtree.WorkingTree.open('.')
125
self.assertEqual(tree.basedir, open_direct.basedir)
126
open_no_args = workingtree.WorkingTree.open()
127
self.assertEqual(tree.basedir, open_no_args.basedir)
129
def test_open_containing(self):
130
tree = self.make_branch_and_tree('.')
131
open_direct, relpath = workingtree.WorkingTree.open_containing('.')
132
self.assertEqual(tree.basedir, open_direct.basedir)
133
self.assertEqual('', relpath)
134
open_no_args, relpath = workingtree.WorkingTree.open_containing()
135
self.assertEqual(tree.basedir, open_no_args.basedir)
136
self.assertEqual('', relpath)
137
open_subdir, relpath = workingtree.WorkingTree.open_containing(
139
self.assertEqual(tree.basedir, open_subdir.basedir)
140
self.assertEqual('subdir', relpath)
143
class SampleTreeFormat(bzrworkingtree.WorkingTreeFormatMetaDir):
146
this format is initializable, unsupported to aid in testing the
147
open and open_downlevel routines.
151
def get_format_string(cls):
152
"""See WorkingTreeFormat.get_format_string()."""
153
return b"Sample tree format."
155
def initialize(self, a_controldir, revision_id=None, from_branch=None,
156
accelerator_tree=None, hardlink=False):
157
"""Sample branches cannot be created."""
158
t = a_controldir.get_workingtree_transport(self)
159
t.put_bytes('format', self.get_format_string())
162
def is_supported(self):
165
def open(self, transport, _found=False):
166
return "opened tree."
169
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
170
"""A sample format that does not support use in a metadir.
174
def get_format_string(self):
175
# Not usable in a metadir, so no format string
178
def initialize(self, a_controldir, revision_id=None, from_branch=None,
179
accelerator_tree=None, hardlink=False):
180
raise NotImplementedError(self.initialize)
182
def is_supported(self):
185
def open(self, transport, _found=False):
186
raise NotImplementedError(self.open)
189
class TestWorkingTreeFormat(TestCaseWithTransport):
190
"""Tests for the WorkingTreeFormat facility."""
192
def test_find_format_string(self):
193
# is the right format object found for a working tree?
194
branch = self.make_branch('branch')
196
errors.NoWorkingTree,
197
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string,
199
transport = branch.controldir.get_workingtree_transport(None)
201
transport.put_bytes("format", b"some format name")
202
# The format does not have to be known by Bazaar,
203
# find_format_string just retrieves the name
206
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string(
209
def test_find_format(self):
210
# is the right format object found for a working tree?
211
# create a branch with a few known format objects.
212
self.build_tree(["foo/", "bar/"])
214
def check_format(format, url):
215
dir = format._matchingcontroldir.initialize(url)
216
dir.create_repository()
218
format.initialize(dir)
219
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
221
self.assertIsInstance(found_format, format.__class__)
222
check_format(workingtree_3.WorkingTreeFormat3(), "bar")
224
def test_find_format_no_tree(self):
225
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
226
self.assertRaises(errors.NoWorkingTree,
227
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
230
def test_find_format_unknown_format(self):
231
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
232
dir.create_repository()
234
SampleTreeFormat().initialize(dir)
235
self.assertRaises(errors.UnknownFormatError,
236
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
239
def test_find_format_with_features(self):
240
tree = self.make_branch_and_tree('.', format='2a')
241
tree.update_feature_flags({b"name": b"necessity"})
242
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
244
self.assertIsInstance(found_format, workingtree.WorkingTreeFormat)
245
self.assertEqual(found_format.features.get(b"name"), b"necessity")
247
bzrdir.MissingFeature, found_format.check_support_status, True)
249
bzrworkingtree.WorkingTreeFormatMetaDir.unregister_feature,
251
bzrworkingtree.WorkingTreeFormatMetaDir.register_feature(b"name")
252
found_format.check_support_status(True)
255
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
257
def make_simple_tree(self):
258
tree = self.make_branch_and_tree('tree', format='development-subtree')
259
self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
260
tree.set_root_id(b'root-id')
261
tree.add(['a', 'a/b', 'a/b/c'], [b'a-id', b'b-id', b'c-id'])
262
tree.commit('initial')
265
def test_just_directory(self):
266
tree = self.make_simple_tree()
267
self.assertEqual([('directory', b'root-id'),
268
('directory', b'a-id'),
269
('directory', b'b-id'),
271
[(ie.kind, ie.file_id)
272
for path, ie in tree.iter_entries_by_dir()])
273
self.make_branch_and_tree('tree/a/b')
274
self.assertEqual([('tree-reference', b'b-id')],
275
[(ie.kind, ie.file_id)
276
for path, ie in tree.iter_entries_by_dir(
277
specific_files=['a/b'])])
279
def test_direct_subtree(self):
280
tree = self.make_simple_tree()
281
self.make_branch_and_tree('tree/a/b')
282
self.assertEqual([('directory', b'root-id'),
283
('directory', b'a-id'),
284
('tree-reference', b'b-id')],
285
[(ie.kind, ie.file_id)
286
for path, ie in tree.iter_entries_by_dir()])
288
def test_indirect_subtree(self):
289
tree = self.make_simple_tree()
290
self.make_branch_and_tree('tree/a')
291
self.assertEqual([('directory', b'root-id'),
292
('tree-reference', b'a-id')],
293
[(ie.kind, ie.file_id)
294
for path, ie in tree.iter_entries_by_dir()])
297
class TestWorkingTreeFormatRegistry(TestCase):
300
super(TestWorkingTreeFormatRegistry, self).setUp()
301
self.registry = workingtree.WorkingTreeFormatRegistry()
303
def test_register_unregister_format(self):
304
format = SampleTreeFormat()
305
self.registry.register(format)
306
self.assertEqual(format, self.registry.get(b"Sample tree format."))
307
self.registry.remove(format)
308
self.assertRaises(KeyError, self.registry.get, b"Sample tree format.")
310
def test_get_all(self):
311
format = SampleTreeFormat()
312
self.assertEqual([], self.registry._get_all())
313
self.registry.register(format)
314
self.assertEqual([format], self.registry._get_all())
316
def test_register_extra(self):
317
format = SampleExtraTreeFormat()
318
self.assertEqual([], self.registry._get_all())
319
self.registry.register_extra(format)
320
self.assertEqual([format], self.registry._get_all())
322
def test_register_extra_lazy(self):
323
self.assertEqual([], self.registry._get_all())
324
self.registry.register_extra_lazy("breezy.tests.test_workingtree",
325
"SampleExtraTreeFormat")
326
formats = self.registry._get_all()
327
self.assertEqual(1, len(formats))
328
self.assertIsInstance(formats[0], SampleExtraTreeFormat)
331
class TestWorkingTreeFormat3(TestCaseWithTransport):
332
"""Tests specific to WorkingTreeFormat3."""
334
def test_disk_layout(self):
335
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
336
control.create_repository()
337
control.create_branch()
338
workingtree_3.WorkingTreeFormat3().initialize(control)
340
# format 'Bazaar-NG Working Tree format 3'
341
# inventory = blank inventory
342
# pending-merges = ''
344
# no inventory.basis yet
345
t = control.get_workingtree_transport(None)
346
self.assertEqualDiff(b'Bazaar-NG Working Tree format 3',
347
t.get('format').read())
348
self.assertEqualDiff(t.get('inventory').read(),
349
b'<inventory format="5">\n'
352
self.assertEqualDiff(b'### bzr hashcache v5\n',
353
t.get('stat-cache').read())
354
self.assertFalse(t.has('inventory.basis'))
355
# no last-revision file means 'None' or 'NULLREVISION'
356
self.assertFalse(t.has('last-revision'))
357
# TODO RBC 20060210 do a commit, check the inventory.basis is created
358
# correctly and last-revision file becomes present.
360
def test_uses_lockdir(self):
361
"""WorkingTreeFormat3 uses its own LockDir:
363
- lock is a directory
364
- when the WorkingTree is locked, LockDir can see that
366
t = self.get_transport()
368
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
369
dir.create_repository()
372
tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
373
except errors.NotLocalUrl:
374
raise TestSkipped('Not a local URL')
375
self.assertIsDirectory('.bzr', t)
376
self.assertIsDirectory('.bzr/checkout', t)
377
self.assertIsDirectory('.bzr/checkout/lock', t)
378
our_lock = LockDir(t, '.bzr/checkout/lock')
379
self.assertEqual(our_lock.peek(), None)
380
with tree.lock_write():
381
self.assertTrue(our_lock.peek())
382
self.assertEqual(our_lock.peek(), None)
384
def test_missing_pending_merges(self):
385
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
386
control.create_repository()
387
control.create_branch()
388
tree = workingtree_3.WorkingTreeFormat3().initialize(control)
389
tree._transport.delete("pending-merges")
390
self.assertEqual([], tree.get_parent_ids())
393
class TestRevert(TestCaseWithTransport):
395
def test_revert_conflicts_recursive(self):
396
this_tree = self.make_branch_and_tree('this-tree')
397
self.build_tree_contents([('this-tree/foo/',),
398
('this-tree/foo/bar', b'bar')])
399
this_tree.add(['foo', 'foo/bar'])
400
this_tree.commit('created foo/bar')
401
other_tree = this_tree.controldir.sprout(
402
'other-tree').open_workingtree()
403
self.build_tree_contents([('other-tree/foo/bar', b'baz')])
404
other_tree.commit('changed bar')
405
self.build_tree_contents([('this-tree/foo/bar', b'qux')])
406
this_tree.commit('changed qux')
407
this_tree.merge_from_branch(other_tree.branch)
408
self.assertEqual(1, len(this_tree.conflicts()))
409
this_tree.revert(['foo'])
410
self.assertEqual(0, len(this_tree.conflicts()))
413
class TestAutoResolve(TestCaseWithTransport):
415
def _auto_resolve(self, tree):
416
"""Call auto_resolve on tree expecting deprecation"""
417
return self.applyDeprecated(
418
symbol_versioning.deprecated_in((3, 0, 1)),
421
def test_auto_resolve(self):
422
base = self.make_branch_and_tree('base')
423
self.build_tree_contents([('base/hello', b'Hello')])
424
base.add('hello', b'hello_id')
426
other = base.controldir.sprout('other').open_workingtree()
427
self.build_tree_contents([('other/hello', b'hELLO')])
428
other.commit('Case switch')
429
this = base.controldir.sprout('this').open_workingtree()
430
self.assertPathExists('this/hello')
431
self.build_tree_contents([('this/hello', b'Hello World')])
432
this.commit('Add World')
433
this.merge_from_branch(other.branch)
434
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
436
self._auto_resolve(this)
437
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
439
self.build_tree_contents([('this/hello', '<<<<<<<')])
440
self._auto_resolve(this)
441
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
443
self.build_tree_contents([('this/hello', '=======')])
444
self._auto_resolve(this)
445
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
447
self.build_tree_contents([('this/hello', '\n>>>>>>>')])
448
remaining, resolved = self._auto_resolve(this)
449
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
451
self.assertEqual([], resolved)
452
self.build_tree_contents([('this/hello', b'hELLO wORLD')])
453
remaining, resolved = self._auto_resolve(this)
454
self.assertEqual([], this.conflicts())
455
self.assertEqual([_mod_bzr_conflicts.TextConflict('hello', b'hello_id')],
457
self.assertPathDoesNotExist('this/hello.BASE')
459
def test_unsupported_symlink_auto_resolve(self):
460
self.requireFeature(SymlinkFeature)
461
base = self.make_branch_and_tree('base')
462
self.build_tree_contents([('base/hello', 'Hello')])
463
base.add('hello', b'hello_id')
464
base.commit('commit 0')
465
other = base.controldir.sprout('other').open_workingtree()
466
self.build_tree_contents([('other/hello', 'Hello')])
467
os.symlink('other/hello', 'other/foo')
468
other.add('foo', b'foo_id')
469
other.commit('commit symlink')
470
this = base.controldir.sprout('this').open_workingtree()
471
self.assertPathExists('this/hello')
472
self.build_tree_contents([('this/hello', 'Hello')])
473
this.commit('commit 2')
475
trace.push_log_file(log)
476
os_symlink = getattr(os, 'symlink', None)
479
this.merge_from_branch(other.branch)
482
os.symlink = os_symlink
483
self.assertContainsRe(
485
b'Unable to create symlink "foo" on this filesystem')
487
def test_auto_resolve_dir(self):
488
tree = self.make_branch_and_tree('tree')
489
self.build_tree(['tree/hello/'])
490
tree.add('hello', b'hello-id')
491
file_conflict = _mod_bzr_conflicts.TextConflict('hello', b'hello-id')
492
tree.set_conflicts([file_conflict])
493
remaining, resolved = self._auto_resolve(tree)
496
conflicts.ConflictList([_mod_bzr_conflicts.TextConflict(u'hello', 'hello-id')]))
497
self.assertEqual(resolved, [])
499
def test_auto_resolve_missing(self):
500
tree = self.make_branch_and_tree('tree')
501
file_conflict = _mod_bzr_conflicts.TextConflict('hello', b'hello-id')
502
tree.set_conflicts([file_conflict])
503
remaining, resolved = self._auto_resolve(tree)
504
self.assertEqual(remaining, [])
507
conflicts.ConflictList([_mod_bzr_conflicts.TextConflict(u'hello', 'hello-id')]))
510
class TestStoredUncommitted(TestCaseWithTransport):
512
def store_uncommitted(self):
513
tree = self.make_branch_and_tree('tree')
514
tree.commit('get root in there')
515
self.build_tree_contents([('tree/file', b'content')])
516
tree.add('file', b'file-id')
517
tree.store_uncommitted()
520
def test_store_uncommitted(self):
521
self.store_uncommitted()
522
self.assertPathDoesNotExist('tree/file')
524
def test_store_uncommitted_no_change(self):
525
tree = self.make_branch_and_tree('tree')
526
tree.commit('get root in there')
527
tree.store_uncommitted()
528
self.assertIs(None, tree.branch.get_unshelver(tree))
530
def test_restore_uncommitted(self):
531
with write_locked(self.store_uncommitted()) as tree:
532
tree.restore_uncommitted()
533
self.assertPathExists('tree/file')
534
self.assertIs(None, tree.branch.get_unshelver(tree))
536
def test_restore_uncommitted_none(self):
537
tree = self.make_branch_and_tree('tree')
538
tree.restore_uncommitted()