/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
# Authors:  Robert Collins <robert.collins@canonical.com>
3
#
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.
8
#
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.
13
#
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
17
18
"""Tests for WorkingTreeFormat4"""
19
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
20
import os
21
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
22
from bzrlib import (
23
    bzrdir,
24
    dirstate,
25
    errors,
1551.15.6 by Aaron Bentley
Use ROOT_ID when the repository supports old clients (Bug #107168)
26
    inventory,
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
27
    workingtree_4,
28
    )
29
from bzrlib.lockdir import LockDir
30
from bzrlib.tests import TestCaseWithTransport, TestSkipped
31
from bzrlib.tree import InterTree
32
33
34
class TestWorkingTreeFormat4(TestCaseWithTransport):
35
    """Tests specific to WorkingTreeFormat4."""
36
37
    def test_disk_layout(self):
38
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
39
        control.create_repository()
40
        control.create_branch()
41
        tree = workingtree_4.WorkingTreeFormat4().initialize(control)
42
        # we want:
43
        # format 'Bazaar Working Tree format 4'
44
        # stat-cache = ??
45
        t = control.get_workingtree_transport(None)
2255.2.230 by Robert Collins
Update tree format signatures to mention introducing bzr version.
46
        self.assertEqualDiff('Bazaar Working Tree Format 4 (bzr 0.15)\n',
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
47
                             t.get('format').read())
48
        self.assertFalse(t.has('inventory.basis'))
49
        # no last-revision file means 'None' or 'NULLREVISION'
50
        self.assertFalse(t.has('last-revision'))
51
        state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
52
        state.lock_read()
53
        try:
54
            self.assertEqual([], state.get_parent_ids())
55
        finally:
56
            state.unlock()
57
58
    def test_uses_lockdir(self):
59
        """WorkingTreeFormat4 uses its own LockDir:
2255.10.1 by John Arbash Meinel
Update WorkingTree4 so that it doesn't use a HashCache,
60
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
61
            - lock is a directory
62
            - when the WorkingTree is locked, LockDir can see that
63
        """
64
        # this test could be factored into a subclass of tests common to both
65
        # format 3 and 4, but for now its not much of an issue as there is only one in common.
66
        t = self.get_transport()
67
        tree = self.make_workingtree()
68
        self.assertIsDirectory('.bzr', t)
69
        self.assertIsDirectory('.bzr/checkout', t)
70
        self.assertIsDirectory('.bzr/checkout/lock', t)
71
        our_lock = LockDir(t, '.bzr/checkout/lock')
72
        self.assertEquals(our_lock.peek(), None)
73
        tree.lock_write()
74
        self.assertTrue(our_lock.peek())
75
        tree.unlock()
76
        self.assertEquals(our_lock.peek(), None)
77
78
    def make_workingtree(self, relpath=''):
79
        url = self.get_url(relpath)
80
        if relpath:
81
            self.build_tree([relpath + '/'])
82
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
83
        repo = dir.create_repository()
84
        branch = dir.create_branch()
85
        try:
86
            return workingtree_4.WorkingTreeFormat4().initialize(dir)
87
        except errors.NotLocalUrl:
88
            raise TestSkipped('Not a local URL')
89
90
    def test_dirstate_stores_all_parent_inventories(self):
91
        tree = self.make_workingtree()
92
93
        # We're going to build in tree a working tree 
94
        # with three parent trees, with some files in common.  
95
    
96
        # We really don't want to do commit or merge in the new dirstate-based
97
        # tree, because that might not work yet.  So instead we build
98
        # revisions elsewhere and pull them across, doing by hand part of the
99
        # work that merge would do.
100
101
        subtree = self.make_branch_and_tree('subdir')
102
        # writelock the tree so its repository doesn't get readlocked by
103
        # the revision tree locks. This works around the bug where we dont
104
        # permit lock upgrading.
105
        subtree.lock_write()
106
        self.addCleanup(subtree.unlock)
107
        self.build_tree(['subdir/file-a',])
108
        subtree.add(['file-a'], ['id-a'])
109
        rev1 = subtree.commit('commit in subdir')
110
111
        subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
112
        self.build_tree(['subdir2/file-b'])
113
        subtree2.add(['file-b'], ['id-b'])
114
        rev2 = subtree2.commit('commit in subdir2')
115
116
        subtree.flush()
117
        subtree.merge_from_branch(subtree2.branch)
118
        rev3 = subtree.commit('merge from subdir2')
119
120
        repo = tree.branch.repository
121
        repo.fetch(subtree.branch.repository, rev3)
122
        # will also pull the others...
123
124
        # create repository based revision trees
125
        rev1_revtree = subtree.branch.repository.revision_tree(rev1)
126
        rev2_revtree = subtree2.branch.repository.revision_tree(rev2)
127
        rev3_revtree = subtree.branch.repository.revision_tree(rev3)
128
        # tree doesn't contain a text merge yet but we'll just
129
        # set the parents as if a merge had taken place. 
130
        # this should cause the tree data to be folded into the 
131
        # dirstate.
132
        tree.set_parent_trees([
133
            (rev1, rev1_revtree),
134
            (rev2, rev2_revtree),
135
            (rev3, rev3_revtree), ])
136
137
        # create tree-sourced revision trees
138
        rev1_tree = tree.revision_tree(rev1)
139
        rev1_tree.lock_read()
140
        self.addCleanup(rev1_tree.unlock)
141
        rev2_tree = tree.revision_tree(rev2)
142
        rev2_tree.lock_read()
143
        self.addCleanup(rev2_tree.unlock)
144
        rev3_tree = tree.revision_tree(rev3)
145
        rev3_tree.lock_read()
146
        self.addCleanup(rev3_tree.unlock)
147
148
        # now we should be able to get them back out
149
        self.assertTreesEqual(rev1_revtree, rev1_tree)
150
        self.assertTreesEqual(rev2_revtree, rev2_tree)
151
        self.assertTreesEqual(rev3_revtree, rev3_tree)
152
153
    def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
154
        """Setting parent trees on a dirstate working tree takes
155
        the trees it's given and doesn't need to read them from the 
156
        repository.
157
        """
158
        tree = self.make_workingtree()
159
160
        subtree = self.make_branch_and_tree('subdir')
161
        rev1 = subtree.commit('commit in subdir')
162
        rev1_tree = subtree.basis_tree()
163
        rev1_tree.lock_read()
164
        self.addCleanup(rev1_tree.unlock)
165
166
        tree.branch.pull(subtree.branch)
167
168
        # break the repository's legs to make sure it only uses the trees
169
        # it's given; any calls to forbidden methods will raise an 
170
        # AssertionError
171
        repo = tree.branch.repository
172
        repo.get_revision = self.fail
173
        repo.get_inventory = self.fail
174
        repo.get_inventory_xml = self.fail
175
        # try to set the parent trees.
176
        tree.set_parent_trees([(rev1, rev1_tree)])
177
178
    def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
179
        """Getting parent trees from a dirstate tree does not read from the 
180
        repos inventory store. This is an important part of the dirstate
181
        performance optimisation work.
182
        """
183
        tree = self.make_workingtree()
184
185
        subtree = self.make_branch_and_tree('subdir')
186
        # writelock the tree so its repository doesn't get readlocked by
187
        # the revision tree locks. This works around the bug where we dont
188
        # permit lock upgrading.
189
        subtree.lock_write()
190
        self.addCleanup(subtree.unlock)
191
        rev1 = subtree.commit('commit in subdir')
192
        rev1_tree = subtree.basis_tree()
193
        rev1_tree.lock_read()
194
        rev1_tree.inventory
195
        self.addCleanup(rev1_tree.unlock)
196
        rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
197
        rev2_tree = subtree.basis_tree()
198
        rev2_tree.lock_read()
199
        rev2_tree.inventory
200
        self.addCleanup(rev2_tree.unlock)
201
202
        tree.branch.pull(subtree.branch)
203
204
        # break the repository's legs to make sure it only uses the trees
205
        # it's given; any calls to forbidden methods will raise an 
206
        # AssertionError
207
        repo = tree.branch.repository
208
        # dont uncomment this: the revision object must be accessed to 
209
        # answer 'get_parent_ids' for the revision tree- dirstate does not 
210
        # cache the parents of a parent tree at this point.
211
        #repo.get_revision = self.fail
212
        repo.get_inventory = self.fail
213
        repo.get_inventory_xml = self.fail
214
        # set the parent trees.
215
        tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
216
        # read the first tree
217
        result_rev1_tree = tree.revision_tree(rev1)
218
        # read the second
219
        result_rev2_tree = tree.revision_tree(rev2)
220
        # compare - there should be no differences between the handed and 
221
        # returned trees
222
        self.assertTreesEqual(rev1_tree, result_rev1_tree)
223
        self.assertTreesEqual(rev2_tree, result_rev2_tree)
224
225
    def test_dirstate_doesnt_cache_non_parent_trees(self):
226
        """Getting parent trees from a dirstate tree does not read from the 
227
        repos inventory store. This is an important part of the dirstate
228
        performance optimisation work.
229
        """
230
        tree = self.make_workingtree()
231
232
        # make a tree that we can try for, which is able to be returned but
233
        # must not be
234
        subtree = self.make_branch_and_tree('subdir')
235
        rev1 = subtree.commit('commit in subdir')
236
        tree.branch.pull(subtree.branch)
237
        # check it fails
238
        self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
239
240
    def test_no_dirstate_outside_lock(self):
241
        # temporary test until the code is mature enough to test from outside.
242
        """Getting a dirstate object fails if there is no lock."""
243
        def lock_and_call_current_dirstate(tree, lock_method):
244
            getattr(tree, lock_method)()
245
            tree.current_dirstate()
246
            tree.unlock()
247
        tree = self.make_workingtree()
248
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
249
        lock_and_call_current_dirstate(tree, 'lock_read')
250
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
251
        lock_and_call_current_dirstate(tree, 'lock_write')
252
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
253
        lock_and_call_current_dirstate(tree, 'lock_tree_write')
254
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
255
256
    def test_new_dirstate_on_new_lock(self):
257
        # until we have detection for when a dirstate can be reused, we
258
        # want to reparse dirstate on every new lock.
259
        known_dirstates = set()
260
        def lock_and_compare_all_current_dirstate(tree, lock_method):
261
            getattr(tree, lock_method)()
262
            state = tree.current_dirstate()
263
            self.assertFalse(state in known_dirstates)
264
            known_dirstates.add(state)
265
            tree.unlock()
266
        tree = self.make_workingtree()
267
        # lock twice with each type to prevent silly per-lock-type bugs.
268
        # each lock and compare looks for a unique state object.
269
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
270
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
271
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
272
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
273
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
274
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
275
2255.2.122 by Robert Collins
Alter intertree implementation tests to let dirstate inter-trees be correctly parameterised.
276
    def test_constructing_invalid_interdirstate_raises(self):
277
        tree = self.make_workingtree()
278
        rev_id = tree.commit('first post')
279
        rev_id2 = tree.commit('second post')
280
        rev_tree = tree.branch.repository.revision_tree(rev_id)
281
        # Exception is not a great thing to raise, but this test is 
282
        # very short, and code is used to sanity check other tests, so 
283
        # a full error object is YAGNI.
284
        self.assertRaises(
285
            Exception, workingtree_4.InterDirStateTree, rev_tree, tree)
286
        self.assertRaises(
287
            Exception, workingtree_4.InterDirStateTree, tree, rev_tree)
288
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
289
    def test_revtree_to_revtree_not_interdirstate(self):
290
        # we should not get a dirstate optimiser for two repository sourced
291
        # revtrees. we can't prove a negative, so we dont do exhaustive tests
292
        # of all formats; though that could be written in the future it doesn't
293
        # seem well worth it.
294
        tree = self.make_workingtree()
295
        rev_id = tree.commit('first post')
296
        rev_id2 = tree.commit('second post')
297
        rev_tree = tree.branch.repository.revision_tree(rev_id)
298
        rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
299
        optimiser = InterTree.get(rev_tree, rev_tree2)
300
        self.assertIsInstance(optimiser, InterTree)
301
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
302
        optimiser = InterTree.get(rev_tree2, rev_tree)
303
        self.assertIsInstance(optimiser, InterTree)
304
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
305
306
    def test_revtree_not_in_dirstate_to_dirstate_not_interdirstate(self):
307
        # we should not get a dirstate optimiser when the revision id for of
308
        # the source is not in the dirstate of the target.
309
        tree = self.make_workingtree()
310
        rev_id = tree.commit('first post')
311
        rev_id2 = tree.commit('second post')
312
        rev_tree = tree.branch.repository.revision_tree(rev_id)
313
        tree.lock_read()
314
        optimiser = InterTree.get(rev_tree, tree)
315
        self.assertIsInstance(optimiser, InterTree)
316
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
317
        optimiser = InterTree.get(tree, rev_tree)
318
        self.assertIsInstance(optimiser, InterTree)
319
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
320
        tree.unlock()
321
322
    def test_empty_basis_to_dirstate_tree(self):
323
        # we should get a InterDirStateTree for doing
324
        # 'changes_from' from the first basis dirstate revision tree to a
325
        # WorkingTree4.
326
        tree = self.make_workingtree()
327
        tree.lock_read()
328
        basis_tree = tree.basis_tree()
329
        basis_tree.lock_read()
330
        optimiser = InterTree.get(basis_tree, tree)
331
        tree.unlock()
332
        basis_tree.unlock()
333
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
334
335
    def test_nonempty_basis_to_dirstate_tree(self):
336
        # we should get a InterDirStateTree for doing
337
        # 'changes_from' from a non-null basis dirstate revision tree to a
338
        # WorkingTree4.
339
        tree = self.make_workingtree()
340
        tree.commit('first post')
341
        tree.lock_read()
342
        basis_tree = tree.basis_tree()
343
        basis_tree.lock_read()
344
        optimiser = InterTree.get(basis_tree, tree)
345
        tree.unlock()
346
        basis_tree.unlock()
347
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
348
349
    def test_empty_basis_revtree_to_dirstate_tree(self):
350
        # we should get a InterDirStateTree for doing
351
        # 'changes_from' from an empty repository based rev tree to a
352
        # WorkingTree4.
353
        tree = self.make_workingtree()
354
        tree.lock_read()
355
        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
356
        basis_tree.lock_read()
357
        optimiser = InterTree.get(basis_tree, tree)
358
        tree.unlock()
359
        basis_tree.unlock()
360
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
361
362
    def test_nonempty_basis_revtree_to_dirstate_tree(self):
363
        # we should get a InterDirStateTree for doing
364
        # 'changes_from' from a non-null repository based rev tree to a
365
        # WorkingTree4.
366
        tree = self.make_workingtree()
367
        tree.commit('first post')
368
        tree.lock_read()
369
        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
370
        basis_tree.lock_read()
371
        optimiser = InterTree.get(basis_tree, tree)
372
        tree.unlock()
373
        basis_tree.unlock()
374
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
375
376
    def test_tree_to_basis_in_other_tree(self):
377
        # we should get a InterDirStateTree when
378
        # the source revid is in the dirstate object of the target and
379
        # the dirstates are different. This is largely covered by testing
380
        # with repository revtrees, so is just for extra confidence.
381
        tree = self.make_workingtree('a')
382
        tree.commit('first post')
383
        tree2 = self.make_workingtree('b')
384
        tree2.pull(tree.branch)
385
        basis_tree = tree.basis_tree()
386
        tree2.lock_read()
387
        basis_tree.lock_read()
388
        optimiser = InterTree.get(basis_tree, tree2)
389
        tree2.unlock()
390
        basis_tree.unlock()
391
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
392
393
    def test_merged_revtree_to_tree(self):
394
        # we should get a InterDirStateTree when
395
        # the source tree is a merged tree present in the dirstate of target.
396
        tree = self.make_workingtree('a')
397
        tree.commit('first post')
398
        tree.commit('tree 1 commit 2')
399
        tree2 = self.make_workingtree('b')
400
        tree2.pull(tree.branch)
401
        tree2.commit('tree 2 commit 2')
402
        tree.merge_from_branch(tree2.branch)
403
        second_parent_tree = tree.revision_tree(tree.get_parent_ids()[1])
404
        second_parent_tree.lock_read()
405
        tree.lock_read()
406
        optimiser = InterTree.get(second_parent_tree, tree)
407
        tree.unlock()
408
        second_parent_tree.unlock()
409
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
410
411
    def test_id2path(self):
412
        tree = self.make_workingtree('tree')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
413
        self.build_tree(['tree/a', 'tree/b'])
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
414
        tree.add(['a'], ['a-id'])
415
        self.assertEqual(u'a', tree.id2path('a-id'))
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
416
        self.assertRaises(errors.NoSuchId, tree.id2path, 'a')
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
417
        tree.commit('a')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
418
        tree.add(['b'], ['b-id'])
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
419
2321.1.2 by Robert Collins
Skip new tests that depend on unicode file paths.
420
        try:
421
            tree.rename_one('a', u'b\xb5rry')
422
            new_path = u'b\xb5rry'
423
        except UnicodeEncodeError:
424
            # support running the test on non-unicode platforms
425
            tree.rename_one('a', 'c')
426
            new_path = 'c'
427
        self.assertEqual(new_path, tree.id2path('a-id'))
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
428
        tree.commit(u'b\xb5rry')
429
        tree.unversion(['a-id'])
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
430
        self.assertRaises(errors.NoSuchId, tree.id2path, 'a-id')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
431
        self.assertEqual('b', tree.id2path('b-id'))
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
432
        self.assertRaises(errors.NoSuchId, tree.id2path, 'c-id')
2255.2.166 by Martin Pool
(broken) Add Tree.get_root_id() & test
433
434
    def test_unique_root_id_per_tree(self):
435
        # each time you initialize a new tree, it gets a different root id
2255.2.207 by Robert Collins
Reinstate format change for test_workingtree_4
436
        format_name = 'dirstate-with-subtree'
2255.2.166 by Martin Pool
(broken) Add Tree.get_root_id() & test
437
        tree1 = self.make_branch_and_tree('tree1',
438
            format=format_name)
439
        tree2 = self.make_branch_and_tree('tree2',
440
            format=format_name)
441
        self.assertNotEqual(tree1.get_root_id(), tree2.get_root_id())
442
        # when you branch, it inherits the same root id
443
        rev1 = tree1.commit('first post')
444
        tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
445
        self.assertEqual(tree3.get_root_id(), tree1.get_root_id())
446
2255.11.2 by Martin Pool
Add more dirstate root-id-changing tests
447
    def test_set_root_id(self):
448
        # similar to some code that fails in the dirstate-plus-subtree branch
449
        # -- setting the root id while adding a parent seems to scramble the
450
        # dirstate invariants. -- mbp 20070303
451
        def validate():
452
            wt.lock_read()
453
            try:
454
                wt.current_dirstate()._validate()
455
            finally:
456
                wt.unlock()
457
        wt = self.make_workingtree('tree')
458
        wt.set_root_id('TREE-ROOTID')
459
        validate()
460
        wt.commit('somenthing')
461
        validate()
462
        # now switch and commit again
463
        wt.set_root_id('tree-rootid')
464
        validate()
465
        wt.commit('again')
466
        validate()
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
467
1551.15.6 by Aaron Bentley
Use ROOT_ID when the repository supports old clients (Bug #107168)
468
    def test_default_root_id(self):
469
        tree = self.make_branch_and_tree('tag', format='dirstate-tags')
470
        self.assertEqual(inventory.ROOT_ID, tree.get_root_id())
471
        tree = self.make_branch_and_tree('subtree',
472
                                         format='dirstate-with-subtree')
473
        self.assertNotEqual(inventory.ROOT_ID, tree.get_root_id())
474
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
475
    def test_non_subtree_with_nested_trees(self):
476
        # prior to dirstate, st/diff/commit ignored nested trees.
477
        # dirstate, as opposed to dirstate-with-subtree, should
478
        # behave the same way.
479
        tree = self.make_branch_and_tree('.', format='dirstate')
480
        self.assertFalse(tree.supports_tree_reference())
481
        self.build_tree(['dir/'])
482
        # for testing easily.
483
        tree.set_root_id('root')
484
        tree.add(['dir'], ['dir-id'])
485
        subtree = self.make_branch_and_tree('dir')
486
        # the most primitive operation: kind
487
        self.assertEqual('directory', tree.kind('dir-id'))
488
        # a diff against the basis should give us a directory
489
        tree.lock_read()
490
        expected = [('dir-id',
491
            (None, u'dir'),
492
            True,
493
            (False, True),
494
            (None, 'root'),
495
            (None, u'dir'),
496
            (None, 'directory'),
497
            (None, False))]
498
        self.assertEqual(expected, list(tree._iter_changes(tree.basis_tree(),
499
            specific_files=['dir'])))
500
        tree.unlock()
501
        # do a commit, we want to trigger the dirstate fast-path too
502
        tree.commit('first post')
503
        # change the path for the subdir, which will trigger getting all
504
        # its data:
505
        os.rename('dir', 'also-dir')
506
        # now the diff will use the fast path
507
        tree.lock_read()
508
        expected = [('dir-id',
509
            (u'dir', u'dir'),
510
            True,
511
            (True, True),
512
            ('root', 'root'),
513
            ('dir', 'dir'),
514
            ('directory', None),
515
            (False, False))]
516
        self.assertEqual(expected, list(tree._iter_changes(tree.basis_tree())))
517
        tree.unlock()
518
519
    def test_with_subtree_supports_tree_references(self):
520
        # dirstate-with-subtree should support tree-references.
521
        tree = self.make_branch_and_tree('.', format='dirstate-with-subtree')
522
        self.assertTrue(tree.supports_tree_reference())
523
        # having checked this is on, the tree interface, and intertree
524
        # interface tests, will proceed to test the subtree support of
525
        # workingtree_4.