1
# Copyright (C) 2005, 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
21
from bzrlib import dirstate, ignores
23
from bzrlib.branch import Branch
24
from bzrlib import bzrdir, conflicts, errors, workingtree, workingtree_4
25
from bzrlib.bzrdir import BzrDir
26
from bzrlib.errors import NotBranchError, NotVersionedError
27
from bzrlib.lockdir import LockDir
28
from bzrlib.mutabletree import needs_tree_write_lock
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.symbol_versioning import zero_thirteen
31
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
32
from bzrlib.trace import mutter
33
from bzrlib.transport import get_transport
34
from bzrlib.tree import InterTree
35
from bzrlib.workingtree import (
43
class TestTreeDirectory(TestCaseWithTransport):
45
def test_kind_character(self):
46
self.assertEqual(TreeDirectory().kind_character(), '/')
49
class TestTreeEntry(TestCaseWithTransport):
51
def test_kind_character(self):
52
self.assertEqual(TreeEntry().kind_character(), '???')
55
class TestTreeFile(TestCaseWithTransport):
57
def test_kind_character(self):
58
self.assertEqual(TreeFile().kind_character(), '')
61
class TestTreeLink(TestCaseWithTransport):
63
def test_kind_character(self):
64
self.assertEqual(TreeLink().kind_character(), '')
67
class TestDefaultFormat(TestCaseWithTransport):
69
def test_get_set_default_format(self):
70
old_format = workingtree.WorkingTreeFormat.get_default_format()
72
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
73
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
75
# the default branch format is used by the meta dir format
76
# which is not the default bzrdir format at this point
77
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
78
dir.create_repository()
80
result = dir.create_workingtree()
81
self.assertEqual(result, 'A tree')
83
workingtree.WorkingTreeFormat.set_default_format(old_format)
84
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
87
class SampleTreeFormat(workingtree.WorkingTreeFormat):
90
this format is initializable, unsupported to aid in testing the
91
open and open_downlevel routines.
94
def get_format_string(self):
95
"""See WorkingTreeFormat.get_format_string()."""
96
return "Sample tree format."
98
def initialize(self, a_bzrdir, revision_id=None):
99
"""Sample branches cannot be created."""
100
t = a_bzrdir.get_workingtree_transport(self)
101
t.put_bytes('format', self.get_format_string())
104
def is_supported(self):
107
def open(self, transport, _found=False):
108
return "opened tree."
111
class TestWorkingTreeFormat(TestCaseWithTransport):
112
"""Tests for the WorkingTreeFormat facility."""
114
def test_find_format(self):
115
# is the right format object found for a working tree?
116
# create a branch with a few known format objects.
117
self.build_tree(["foo/", "bar/"])
118
def check_format(format, url):
119
dir = format._matchingbzrdir.initialize(url)
120
dir.create_repository()
122
format.initialize(dir)
123
t = get_transport(url)
124
found_format = workingtree.WorkingTreeFormat.find_format(dir)
125
self.failUnless(isinstance(found_format, format.__class__))
126
check_format(workingtree.WorkingTreeFormat3(), "bar")
128
def test_find_format_no_tree(self):
129
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
130
self.assertRaises(errors.NoWorkingTree,
131
workingtree.WorkingTreeFormat.find_format,
134
def test_find_format_unknown_format(self):
135
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
136
dir.create_repository()
138
SampleTreeFormat().initialize(dir)
139
self.assertRaises(errors.UnknownFormatError,
140
workingtree.WorkingTreeFormat.find_format,
143
def test_register_unregister_format(self):
144
format = SampleTreeFormat()
146
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
147
dir.create_repository()
150
format.initialize(dir)
151
# register a format for it.
152
workingtree.WorkingTreeFormat.register_format(format)
153
# which branch.Open will refuse (not supported)
154
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
155
# but open_downlevel will work
156
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
157
# unregister the format
158
workingtree.WorkingTreeFormat.unregister_format(format)
161
class TestWorkingTreeFormat3(TestCaseWithTransport):
162
"""Tests specific to WorkingTreeFormat3."""
164
def test_disk_layout(self):
165
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
166
control.create_repository()
167
control.create_branch()
168
tree = workingtree.WorkingTreeFormat3().initialize(control)
170
# format 'Bazaar-NG Working Tree format 3'
171
# inventory = blank inventory
172
# pending-merges = ''
174
# no inventory.basis yet
175
t = control.get_workingtree_transport(None)
176
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
177
t.get('format').read())
178
# self.assertContainsRe(t.get('inventory').read(),
179
# '<inventory file_id="[^"]*" format="5">\n'
182
# WorkingTreeFormat3 doesn't default to creating a unique root id,
183
# because it is incompatible with older bzr versions
184
self.assertContainsRe(t.get('inventory').read(),
185
'<inventory format="5">\n'
188
self.assertEqualDiff('### bzr hashcache v5\n',
189
t.get('stat-cache').read())
190
self.assertFalse(t.has('inventory.basis'))
191
# no last-revision file means 'None' or 'NULLREVISION'
192
self.assertFalse(t.has('last-revision'))
193
# TODO RBC 20060210 do a commit, check the inventory.basis is created
194
# correctly and last-revision file becomes present.
196
def test_uses_lockdir(self):
197
"""WorkingTreeFormat3 uses its own LockDir:
199
- lock is a directory
200
- when the WorkingTree is locked, LockDir can see that
202
t = self.get_transport()
204
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
205
repo = dir.create_repository()
206
branch = dir.create_branch()
208
tree = workingtree.WorkingTreeFormat3().initialize(dir)
209
except errors.NotLocalUrl:
210
raise TestSkipped('Not a local URL')
211
self.assertIsDirectory('.bzr', t)
212
self.assertIsDirectory('.bzr/checkout', t)
213
self.assertIsDirectory('.bzr/checkout/lock', t)
214
our_lock = LockDir(t, '.bzr/checkout/lock')
215
self.assertEquals(our_lock.peek(), None)
217
self.assertTrue(our_lock.peek())
219
self.assertEquals(our_lock.peek(), None)
221
def test_missing_pending_merges(self):
222
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
223
control.create_repository()
224
control.create_branch()
225
tree = workingtree.WorkingTreeFormat3().initialize(control)
226
tree._control_files._transport.delete("pending-merges")
227
self.assertEqual([], tree.get_parent_ids())
230
class TestWorkingTreeFormat4(TestCaseWithTransport):
231
"""Tests specific to WorkingTreeFormat4."""
233
def test_disk_layout(self):
234
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
235
control.create_repository()
236
control.create_branch()
237
tree = workingtree_4.WorkingTreeFormat4().initialize(control)
239
# format 'Bazaar Working Tree format 4'
241
t = control.get_workingtree_transport(None)
242
self.assertEqualDiff('Bazaar Working Tree format 4\n',
243
t.get('format').read())
244
self.assertEqualDiff('### bzr hashcache v5\n',
245
t.get('stat-cache').read())
246
self.assertFalse(t.has('inventory.basis'))
247
# no last-revision file means 'None' or 'NULLREVISION'
248
self.assertFalse(t.has('last-revision'))
249
# TODO RBC 20060210 do a commit, check the inventory.basis is created
250
# correctly and last-revision file becomes present.
251
# manually make a dirstate toc check the format is as desired.
252
state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
253
self.assertEqual([], state.get_parent_ids())
255
def test_uses_lockdir(self):
256
"""WorkingTreeFormat4 uses its own LockDir:
258
- lock is a directory
259
- when the WorkingTree is locked, LockDir can see that
261
# this test could be factored into a subclass of tests common to both
262
# format 3 and 4, but for now its not much of an issue as there is only one in common.
263
t = self.get_transport()
264
tree = self.make_workingtree()
265
self.assertIsDirectory('.bzr', t)
266
self.assertIsDirectory('.bzr/checkout', t)
267
self.assertIsDirectory('.bzr/checkout/lock', t)
268
our_lock = LockDir(t, '.bzr/checkout/lock')
269
self.assertEquals(our_lock.peek(), None)
271
self.assertTrue(our_lock.peek())
273
self.assertEquals(our_lock.peek(), None)
275
def make_workingtree(self, relpath=''):
276
url = self.get_url(relpath)
278
self.build_tree([relpath + '/'])
279
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
280
repo = dir.create_repository()
281
branch = dir.create_branch()
283
return workingtree_4.WorkingTreeFormat4().initialize(dir)
284
except errors.NotLocalUrl:
285
raise TestSkipped('Not a local URL')
287
# TODO: test that dirstate also stores & retrieves the parent list of
288
# workingtree-parent revisions, including when they have multiple parents.
289
# (in other words, the case when we're constructing a merge of
290
# revisions which are themselves merges.)
292
# The simplest case is that the the workingtree's primary
293
# parent tree can be retrieved. This is required for all WorkingTrees,
294
# and covered by the generic tests.
296
def test_dirstate_stores_all_parent_inventories(self):
297
tree = self.make_workingtree()
299
# We're going to build in tree a working tree
300
# with three parent trees, with some files in common.
302
# We really don't want to do commit or merge in the new dirstate-based
303
# tree, because that might not work yet. So instead we build
304
# revisions elsewhere and pull them across, doing by hand part of the
305
# work that merge would do.
307
subtree = self.make_branch_and_tree('subdir')
308
# writelock the tree so its repository doesn't get readlocked by
309
# the revision tree locks. This works around the bug where we dont
310
# permit lock upgrading.
312
self.addCleanup(subtree.unlock)
313
self.build_tree(['subdir/file-a',])
314
subtree.add(['file-a'], ['id-a'])
315
rev1 = subtree.commit('commit in subdir')
317
subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
318
self.build_tree(['subdir2/file-b'])
319
subtree2.add(['file-b'], ['id-b'])
320
rev2 = subtree2.commit('commit in subdir2')
323
subtree.merge_from_branch(subtree2.branch)
324
rev3 = subtree.commit('merge from subdir2')
326
repo = tree.branch.repository
327
repo.fetch(subtree.branch.repository, rev3)
328
# will also pull the others...
330
# create repository based revision trees
331
rev1_revtree = subtree.branch.repository.revision_tree(rev1)
332
rev2_revtree = subtree2.branch.repository.revision_tree(rev2)
333
rev3_revtree = subtree.branch.repository.revision_tree(rev3)
334
# tree doesn't contain a text merge yet but we'll just
335
# set the parents as if a merge had taken place.
336
# this should cause the tree data to be folded into the
338
tree.set_parent_trees([
339
(rev1, rev1_revtree),
340
(rev2, rev2_revtree),
341
(rev3, rev3_revtree), ])
343
# create tree-sourced revision trees
344
rev1_tree = tree.revision_tree(rev1)
345
rev1_tree.lock_read()
346
self.addCleanup(rev1_tree.unlock)
347
rev2_tree = tree.revision_tree(rev2)
348
rev2_tree.lock_read()
349
self.addCleanup(rev2_tree.unlock)
350
rev3_tree = tree.revision_tree(rev3)
351
rev3_tree.lock_read()
352
self.addCleanup(rev3_tree.unlock)
354
# now we should be able to get them back out
355
self.assertTreesEqual(rev1_revtree, rev1_tree)
356
self.assertTreesEqual(rev2_revtree, rev2_tree)
357
self.assertTreesEqual(rev3_revtree, rev3_tree)
359
def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
360
"""Setting parent trees on a dirstate working tree takes
361
the trees it's given and doesn't need to read them from the
364
tree = self.make_workingtree()
366
subtree = self.make_branch_and_tree('subdir')
367
rev1 = subtree.commit('commit in subdir')
368
rev1_tree = subtree.basis_tree()
369
rev1_tree.lock_read()
370
self.addCleanup(rev1_tree.unlock)
372
tree.branch.pull(subtree.branch)
374
# break the repository's legs to make sure it only uses the trees
375
# it's given; any calls to forbidden methods will raise an
377
repo = tree.branch.repository
378
repo.get_revision = self.fail
379
repo.get_inventory = self.fail
380
repo.get_inventory_xml = self.fail
381
# try to set the parent trees.
382
tree.set_parent_trees([(rev1, rev1_tree)])
384
def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
385
"""Getting parent trees from a dirstate tree does not read from the
386
repos inventory store. This is an important part of the dirstate
387
performance optimisation work.
389
tree = self.make_workingtree()
391
subtree = self.make_branch_and_tree('subdir')
392
# writelock the tree so its repository doesn't get readlocked by
393
# the revision tree locks. This works around the bug where we dont
394
# permit lock upgrading.
396
self.addCleanup(subtree.unlock)
397
rev1 = subtree.commit('commit in subdir')
398
rev1_tree = subtree.basis_tree()
399
rev1_tree.lock_read()
401
self.addCleanup(rev1_tree.unlock)
402
rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
403
rev2_tree = subtree.basis_tree()
404
rev2_tree.lock_read()
406
self.addCleanup(rev2_tree.unlock)
408
tree.branch.pull(subtree.branch)
410
# break the repository's legs to make sure it only uses the trees
411
# it's given; any calls to forbidden methods will raise an
413
repo = tree.branch.repository
414
# dont uncomment this: the revision object must be accessed to
415
# answer 'get_parent_ids' for the revision tree- dirstate does not
416
# cache the parents of a parent tree at this point.
417
#repo.get_revision = self.fail
418
repo.get_inventory = self.fail
419
repo.get_inventory_xml = self.fail
420
# set the parent trees.
421
tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
422
# read the first tree
423
result_rev1_tree = tree.revision_tree(rev1)
425
result_rev2_tree = tree.revision_tree(rev2)
426
# compare - there should be no differences between the handed and
428
self.assertTreesEqual(rev1_tree, result_rev1_tree)
429
self.assertTreesEqual(rev2_tree, result_rev2_tree)
431
def test_dirstate_doesnt_cache_non_parent_trees(self):
432
"""Getting parent trees from a dirstate tree does not read from the
433
repos inventory store. This is an important part of the dirstate
434
performance optimisation work.
436
tree = self.make_workingtree()
438
# make a tree that we can try for, which is able to be returned but
440
subtree = self.make_branch_and_tree('subdir')
441
rev1 = subtree.commit('commit in subdir')
442
tree.branch.pull(subtree.branch)
444
self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
446
def test_no_dirstate_outside_lock(self):
447
# temporary test until the code is mature enough to test from outside.
448
"""Getting a dirstate object fails if there is no lock."""
449
def lock_and_call_current_dirstate(tree, lock_method):
450
getattr(tree, lock_method)()
451
tree.current_dirstate()
453
tree = self.make_workingtree()
454
self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
455
lock_and_call_current_dirstate(tree, 'lock_read')
456
self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
457
lock_and_call_current_dirstate(tree, 'lock_write')
458
self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
459
lock_and_call_current_dirstate(tree, 'lock_tree_write')
460
self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
462
def test_new_dirstate_on_new_lock(self):
463
# until we have detection for when a dirstate can be reused, we
464
# want to reparse dirstate on every new lock.
465
known_dirstates = set()
466
def lock_and_compare_all_current_dirstate(tree, lock_method):
467
getattr(tree, lock_method)()
468
state = tree.current_dirstate()
469
self.assertFalse(state in known_dirstates)
470
known_dirstates.add(state)
472
tree = self.make_workingtree()
473
# lock twice with each type to prevent silly per-lock-type bugs.
474
# each lock and compare looks for a unique state object.
475
lock_and_compare_all_current_dirstate(tree, 'lock_read')
476
lock_and_compare_all_current_dirstate(tree, 'lock_read')
477
lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
478
lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
479
lock_and_compare_all_current_dirstate(tree, 'lock_write')
480
lock_and_compare_all_current_dirstate(tree, 'lock_write')
482
def test_revtree_to_revtree_not_interdirstate(self):
483
# we should not get a dirstate optimiser for two repository sourced
484
# revtrees. we can't prove a negative, so we dont do exhaustive tests
485
# of all formats; though that could be written in the future it doesn't
486
# seem well worth it.
487
tree = self.make_workingtree()
488
rev_id = tree.commit('first post')
489
rev_id2 = tree.commit('second post')
490
rev_tree = tree.branch.repository.revision_tree(rev_id)
491
rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
492
optimiser = InterTree.get(rev_tree, rev_tree2)
493
self.assertIsInstance(optimiser, InterTree)
494
self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
495
optimiser = InterTree.get(rev_tree2, rev_tree)
496
self.assertIsInstance(optimiser, InterTree)
497
self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
499
def test_revtree_not_in_dirstate_to_dirstate_not_interdirstate(self):
500
# we should not get a dirstate optimiser when the revision id for of
501
# the source is not in the dirstate of the target.
502
tree = self.make_workingtree()
503
rev_id = tree.commit('first post')
504
rev_id2 = tree.commit('second post')
505
rev_tree = tree.branch.repository.revision_tree(rev_id)
507
optimiser = InterTree.get(rev_tree, tree)
508
self.assertIsInstance(optimiser, InterTree)
509
self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
510
optimiser = InterTree.get(tree, rev_tree)
511
self.assertIsInstance(optimiser, InterTree)
512
self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
515
def test_empty_basis_to_dirstate_tree(self):
516
# we should get a InterDirStateTree for doing
517
# 'changes_from' from the first basis dirstate revision tree to a
519
tree = self.make_workingtree()
521
basis_tree = tree.basis_tree()
522
basis_tree.lock_read()
523
optimiser = InterTree.get(basis_tree, tree)
526
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
528
def test_nonempty_basis_to_dirstate_tree(self):
529
# we should get a InterDirStateTree for doing
530
# 'changes_from' from a non-null basis dirstate revision tree to a
532
tree = self.make_workingtree()
533
tree.commit('first post')
535
basis_tree = tree.basis_tree()
536
basis_tree.lock_read()
537
optimiser = InterTree.get(basis_tree, tree)
540
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
542
def test_empty_basis_revtree_to_dirstate_tree(self):
543
# we should get a InterDirStateTree for doing
544
# 'changes_from' from an empty repository based rev tree to a
546
tree = self.make_workingtree()
548
basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
549
basis_tree.lock_read()
550
optimiser = InterTree.get(basis_tree, tree)
553
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
555
def test_nonempty_basis_revtree_to_dirstate_tree(self):
556
# we should get a InterDirStateTree for doing
557
# 'changes_from' from a non-null repository based rev tree to a
559
tree = self.make_workingtree()
560
tree.commit('first post')
562
basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
563
basis_tree.lock_read()
564
optimiser = InterTree.get(basis_tree, tree)
567
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
569
def test_tree_to_basis_in_other_tree(self):
570
# we should get a InterDirStateTree when
571
# the source revid is in the dirstate object of the target and
572
# the dirstates are different. This is largely covered by testing
573
# with repository revtrees, so is just for extra confidence.
574
tree = self.make_workingtree('a')
575
tree.commit('first post')
576
tree2 = self.make_workingtree('b')
577
tree2.pull(tree.branch)
578
basis_tree = tree.basis_tree()
580
basis_tree.lock_read()
581
optimiser = InterTree.get(basis_tree, tree2)
584
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
586
def test_merged_revtree_to_tree(self):
587
# we should get a InterDirStateTree when
588
# the source tree is a merged tree present in the dirstate of target.
589
tree = self.make_workingtree('a')
590
tree.commit('first post')
591
tree.commit('tree 1 commit 2')
592
tree2 = self.make_workingtree('b')
593
tree2.pull(tree.branch)
594
tree2.commit('tree 2 commit 2')
595
tree.merge_from_branch(tree2.branch)
596
second_parent_tree = tree.revision_tree(tree.get_parent_ids()[1])
597
second_parent_tree.lock_read()
599
optimiser = InterTree.get(second_parent_tree, tree)
601
second_parent_tree.unlock()
602
self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
605
class TestFormat2WorkingTree(TestCaseWithTransport):
606
"""Tests that are specific to format 2 trees."""
608
def create_format2_tree(self, url):
609
return self.make_branch_and_tree(
610
url, format=bzrlib.bzrdir.BzrDirFormat6())
612
def test_conflicts(self):
613
# test backwards compatability
614
tree = self.create_format2_tree('.')
615
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
617
file('lala.BASE', 'wb').write('labase')
618
expected = conflicts.ContentsConflict('lala')
619
self.assertEqual(list(tree.conflicts()), [expected])
620
file('lala', 'wb').write('la')
621
tree.add('lala', 'lala-id')
622
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
623
self.assertEqual(list(tree.conflicts()), [expected])
624
file('lala.THIS', 'wb').write('lathis')
625
file('lala.OTHER', 'wb').write('laother')
626
# When "text conflict"s happen, stem, THIS and OTHER are text
627
expected = conflicts.TextConflict('lala', file_id='lala-id')
628
self.assertEqual(list(tree.conflicts()), [expected])
629
os.unlink('lala.OTHER')
630
os.mkdir('lala.OTHER')
631
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
632
self.assertEqual(list(tree.conflicts()), [expected])
635
class TestNonFormatSpecificCode(TestCaseWithTransport):
636
"""This class contains tests of workingtree that are not format specific."""
638
def test_gen_file_id(self):
639
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
641
self.assertStartsWith(file_id, 'filename-')
643
def test_gen_root_id(self):
644
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
645
self.assertStartsWith(file_id, 'tree_root-')
648
class InstrumentedTree(object):
649
"""A instrumented tree to check the needs_tree_write_lock decorator."""
654
def lock_tree_write(self):
655
self._locks.append('t')
657
@needs_tree_write_lock
658
def method_with_tree_write_lock(self, *args, **kwargs):
659
"""A lock_tree_write decorated method that returns its arguments."""
662
@needs_tree_write_lock
663
def method_that_raises(self):
664
"""This method causes an exception when called with parameters.
666
This allows the decorator code to be checked - it should still call
671
self._locks.append('u')
674
class TestInstrumentedTree(TestCase):
676
def test_needs_tree_write_lock(self):
677
"""@needs_tree_write_lock should be semantically transparent."""
678
tree = InstrumentedTree()
680
'method_with_tree_write_lock',
681
tree.method_with_tree_write_lock.__name__)
683
"A lock_tree_write decorated method that returns its arguments.",
684
tree.method_with_tree_write_lock.__doc__)
687
result = tree.method_with_tree_write_lock(1,2,3, a='b')
688
self.assertEqual((args, kwargs), result)
689
self.assertEqual(['t', 'u'], tree._locks)
690
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
691
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)