~bzr-pqm/bzr/bzr.dev

6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
1
# Copyright (C) 2007-2012, 2016 Canonical Ltd
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
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
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
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
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
21
import time
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
22
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
23
from bzrlib import (
24
    bzrdir,
25
    dirstate,
26
    errors,
1551.15.6 by Aaron Bentley
Use ROOT_ID when the repository supports old clients (Bug #107168)
27
    inventory,
2466.4.1 by John Arbash Meinel
Add a (failing) test that exposes how _iter_changes is accidentally walking into unversioned directories.
28
    osutils,
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
29
    workingtree_4,
30
    )
31
from bzrlib.lockdir import LockDir
6345.1.2 by Martin Packman
Add targetted working tree tests for places that raise PathsNotVersionedError
32
from bzrlib.tests import TestCaseWithTransport, TestSkipped, features
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
33
from bzrlib.tree import InterTree
34
35
36
class TestWorkingTreeFormat4(TestCaseWithTransport):
37
    """Tests specific to WorkingTreeFormat4."""
38
39
    def test_disk_layout(self):
40
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
41
        control.create_repository()
42
        control.create_branch()
43
        tree = workingtree_4.WorkingTreeFormat4().initialize(control)
44
        # we want:
45
        # format 'Bazaar Working Tree format 4'
46
        # stat-cache = ??
47
        t = control.get_workingtree_transport(None)
2255.2.230 by Robert Collins
Update tree format signatures to mention introducing bzr version.
48
        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
49
                             t.get('format').read())
50
        self.assertFalse(t.has('inventory.basis'))
51
        # no last-revision file means 'None' or 'NULLREVISION'
52
        self.assertFalse(t.has('last-revision'))
53
        state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
54
        state.lock_read()
55
        try:
56
            self.assertEqual([], state.get_parent_ids())
57
        finally:
58
            state.unlock()
59
5900.1.1 by Jelmer Vernooij
Make sure only the last unlock resets the cached ignore glob.
60
    def test_resets_ignores_on_last_unlock(self):
61
        # Only the last unlock call will actually reset the
62
        # ignores. (bug #785671)
63
        tree = self.make_workingtree()
64
        tree.lock_read()
65
        try:
66
            tree.lock_read()
67
            try:
68
                tree.is_ignored("foo")
69
            finally:
70
                tree.unlock()
71
            self.assertIsNot(None, tree._ignoreglobster)
72
        finally:
73
            tree.unlock()
74
        self.assertIs(None, tree._ignoreglobster)
75
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
76
    def test_uses_lockdir(self):
77
        """WorkingTreeFormat4 uses its own LockDir:
2255.10.1 by John Arbash Meinel
Update WorkingTree4 so that it doesn't use a HashCache,
78
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
79
            - lock is a directory
80
            - when the WorkingTree is locked, LockDir can see that
81
        """
82
        # this test could be factored into a subclass of tests common to both
4285.2.1 by Vincent Ladeuil
Cleanup test imports and use features to better track skipped tests.
83
        # format 3 and 4, but for now its not much of an issue as there is only
84
        # one in common.
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
85
        t = self.get_transport()
86
        tree = self.make_workingtree()
87
        self.assertIsDirectory('.bzr', t)
88
        self.assertIsDirectory('.bzr/checkout', t)
89
        self.assertIsDirectory('.bzr/checkout/lock', t)
90
        our_lock = LockDir(t, '.bzr/checkout/lock')
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
91
        self.assertEqual(our_lock.peek(), None)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
92
        tree.lock_write()
93
        self.assertTrue(our_lock.peek())
94
        tree.unlock()
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
95
        self.assertEqual(our_lock.peek(), None)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
96
97
    def make_workingtree(self, relpath=''):
98
        url = self.get_url(relpath)
99
        if relpath:
100
            self.build_tree([relpath + '/'])
101
        dir = bzrdir.BzrDirMetaFormat1().initialize(url)
102
        repo = dir.create_repository()
103
        branch = dir.create_branch()
104
        try:
105
            return workingtree_4.WorkingTreeFormat4().initialize(dir)
106
        except errors.NotLocalUrl:
107
            raise TestSkipped('Not a local URL')
108
109
    def test_dirstate_stores_all_parent_inventories(self):
110
        tree = self.make_workingtree()
111
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
112
        # We're going to build in tree a working tree
113
        # with three parent trees, with some files in common.
114
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
115
        # We really don't want to do commit or merge in the new dirstate-based
116
        # tree, because that might not work yet.  So instead we build
117
        # revisions elsewhere and pull them across, doing by hand part of the
118
        # work that merge would do.
119
120
        subtree = self.make_branch_and_tree('subdir')
121
        # writelock the tree so its repository doesn't get readlocked by
122
        # the revision tree locks. This works around the bug where we dont
123
        # permit lock upgrading.
124
        subtree.lock_write()
125
        self.addCleanup(subtree.unlock)
126
        self.build_tree(['subdir/file-a',])
127
        subtree.add(['file-a'], ['id-a'])
128
        rev1 = subtree.commit('commit in subdir')
129
130
        subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
131
        self.build_tree(['subdir2/file-b'])
132
        subtree2.add(['file-b'], ['id-b'])
133
        rev2 = subtree2.commit('commit in subdir2')
134
135
        subtree.flush()
3462.1.7 by John Arbash Meinel
fix a test that assumed WT4.set_parent_trees() wouldn't filter the list.
136
        subtree3 = subtree.bzrdir.sprout('subdir3').open_workingtree()
137
        rev3 = subtree3.commit('merge from subdir2')
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
138
139
        repo = tree.branch.repository
3462.1.7 by John Arbash Meinel
fix a test that assumed WT4.set_parent_trees() wouldn't filter the list.
140
        repo.fetch(subtree.branch.repository, rev1)
141
        repo.fetch(subtree2.branch.repository, rev2)
142
        repo.fetch(subtree3.branch.repository, rev3)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
143
        # will also pull the others...
144
145
        # create repository based revision trees
3462.1.7 by John Arbash Meinel
fix a test that assumed WT4.set_parent_trees() wouldn't filter the list.
146
        rev1_revtree = repo.revision_tree(rev1)
147
        rev2_revtree = repo.revision_tree(rev2)
148
        rev3_revtree = repo.revision_tree(rev3)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
149
        # tree doesn't contain a text merge yet but we'll just
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
150
        # set the parents as if a merge had taken place.
151
        # this should cause the tree data to be folded into the
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
152
        # dirstate.
153
        tree.set_parent_trees([
154
            (rev1, rev1_revtree),
155
            (rev2, rev2_revtree),
156
            (rev3, rev3_revtree), ])
157
158
        # create tree-sourced revision trees
159
        rev1_tree = tree.revision_tree(rev1)
160
        rev1_tree.lock_read()
161
        self.addCleanup(rev1_tree.unlock)
162
        rev2_tree = tree.revision_tree(rev2)
163
        rev2_tree.lock_read()
164
        self.addCleanup(rev2_tree.unlock)
165
        rev3_tree = tree.revision_tree(rev3)
166
        rev3_tree.lock_read()
167
        self.addCleanup(rev3_tree.unlock)
168
169
        # now we should be able to get them back out
170
        self.assertTreesEqual(rev1_revtree, rev1_tree)
171
        self.assertTreesEqual(rev2_revtree, rev2_tree)
172
        self.assertTreesEqual(rev3_revtree, rev3_tree)
173
174
    def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
175
        """Setting parent trees on a dirstate working tree takes
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
176
        the trees it's given and doesn't need to read them from the
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
177
        repository.
178
        """
179
        tree = self.make_workingtree()
180
181
        subtree = self.make_branch_and_tree('subdir')
182
        rev1 = subtree.commit('commit in subdir')
183
        rev1_tree = subtree.basis_tree()
184
        rev1_tree.lock_read()
185
        self.addCleanup(rev1_tree.unlock)
186
187
        tree.branch.pull(subtree.branch)
188
189
        # break the repository's legs to make sure it only uses the trees
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
190
        # it's given; any calls to forbidden methods will raise an
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
191
        # AssertionError
192
        repo = tree.branch.repository
5340.15.1 by John Arbash Meinel
supersede exc-info branch
193
        self.overrideAttr(repo, "get_revision", self.fail)
194
        self.overrideAttr(repo, "get_inventory", self.fail)
195
        self.overrideAttr(repo, "_get_inventory_xml", self.fail)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
196
        # try to set the parent trees.
197
        tree.set_parent_trees([(rev1, rev1_tree)])
198
199
    def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
200
        """Getting parent trees from a dirstate tree does not read from the
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
201
        repos inventory store. This is an important part of the dirstate
202
        performance optimisation work.
203
        """
204
        tree = self.make_workingtree()
205
206
        subtree = self.make_branch_and_tree('subdir')
207
        # writelock the tree so its repository doesn't get readlocked by
208
        # the revision tree locks. This works around the bug where we dont
209
        # permit lock upgrading.
210
        subtree.lock_write()
211
        self.addCleanup(subtree.unlock)
212
        rev1 = subtree.commit('commit in subdir')
213
        rev1_tree = subtree.basis_tree()
214
        rev1_tree.lock_read()
6405.2.12 by Jelmer Vernooij
Fix tests.
215
        rev1_tree.root_inventory
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
216
        self.addCleanup(rev1_tree.unlock)
217
        rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
218
        rev2_tree = subtree.basis_tree()
219
        rev2_tree.lock_read()
6405.2.12 by Jelmer Vernooij
Fix tests.
220
        rev2_tree.root_inventory
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
221
        self.addCleanup(rev2_tree.unlock)
222
223
        tree.branch.pull(subtree.branch)
224
225
        # break the repository's legs to make sure it only uses the trees
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
226
        # it's given; any calls to forbidden methods will raise an
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
227
        # AssertionError
228
        repo = tree.branch.repository
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
229
        # dont uncomment this: the revision object must be accessed to
230
        # answer 'get_parent_ids' for the revision tree- dirstate does not
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
231
        # cache the parents of a parent tree at this point.
232
        #repo.get_revision = self.fail
5340.15.1 by John Arbash Meinel
supersede exc-info branch
233
        self.overrideAttr(repo, "get_inventory", self.fail)
234
        self.overrideAttr(repo, "_get_inventory_xml", self.fail)
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
235
        # set the parent trees.
236
        tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
237
        # read the first tree
238
        result_rev1_tree = tree.revision_tree(rev1)
239
        # read the second
240
        result_rev2_tree = tree.revision_tree(rev2)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
241
        # compare - there should be no differences between the handed and
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
242
        # returned trees
243
        self.assertTreesEqual(rev1_tree, result_rev1_tree)
244
        self.assertTreesEqual(rev2_tree, result_rev2_tree)
245
246
    def test_dirstate_doesnt_cache_non_parent_trees(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
247
        """Getting parent trees from a dirstate tree does not read from the
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
248
        repos inventory store. This is an important part of the dirstate
249
        performance optimisation work.
250
        """
251
        tree = self.make_workingtree()
252
253
        # make a tree that we can try for, which is able to be returned but
254
        # must not be
255
        subtree = self.make_branch_and_tree('subdir')
256
        rev1 = subtree.commit('commit in subdir')
257
        tree.branch.pull(subtree.branch)
258
        # check it fails
259
        self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
260
261
    def test_no_dirstate_outside_lock(self):
262
        # temporary test until the code is mature enough to test from outside.
263
        """Getting a dirstate object fails if there is no lock."""
264
        def lock_and_call_current_dirstate(tree, lock_method):
265
            getattr(tree, lock_method)()
266
            tree.current_dirstate()
267
            tree.unlock()
268
        tree = self.make_workingtree()
269
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
270
        lock_and_call_current_dirstate(tree, 'lock_read')
271
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
272
        lock_and_call_current_dirstate(tree, 'lock_write')
273
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
274
        lock_and_call_current_dirstate(tree, 'lock_tree_write')
275
        self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
276
5847.4.1 by John Arbash Meinel
When WT4.set_parent_trees() is called, sometimes we can use
277
    def test_set_parent_trees_uses_update_basis_by_delta(self):
278
        builder = self.make_branch_builder('source')
279
        builder.start_series()
280
        self.addCleanup(builder.finish_series)
281
        builder.build_snapshot('A', [], [
282
            ('add', ('', 'root-id', 'directory', None)),
283
            ('add', ('a', 'a-id', 'file', 'content\n'))])
284
        builder.build_snapshot('B', ['A'], [
285
            ('modify', ('a-id', 'new content\nfor a\n')),
286
            ('add', ('b', 'b-id', 'file', 'b-content\n'))])
287
        tree = self.make_workingtree('tree')
288
        source_branch = builder.get_branch()
289
        tree.branch.repository.fetch(source_branch.repository, 'B')
290
        tree.pull(source_branch, stop_revision='A')
291
        tree.lock_write()
292
        self.addCleanup(tree.unlock)
293
        state = tree.current_dirstate()
294
        called = []
295
        orig_update = state.update_basis_by_delta
296
        def log_update_basis_by_delta(delta, new_revid):
297
            called.append(new_revid)
298
            return orig_update(delta, new_revid)
299
        state.update_basis_by_delta = log_update_basis_by_delta
300
        basis = tree.basis_tree()
301
        self.assertEqual('a-id', basis.path2id('a'))
302
        self.assertEqual(None, basis.path2id('b'))
303
        def fail_set_parent_trees(trees, ghosts):
304
            raise AssertionError('dirstate.set_parent_trees() was called')
305
        state.set_parent_trees = fail_set_parent_trees
306
        repo = tree.branch.repository
307
        tree.pull(source_branch, stop_revision='B')
308
        self.assertEqual(['B'], called)
309
        basis = tree.basis_tree()
310
        self.assertEqual('a-id', basis.path2id('a'))
311
        self.assertEqual('b-id', basis.path2id('b'))
312
5847.4.10 by John Arbash Meinel
A few more bug fixes.
313
    def test_set_parent_trees_handles_missing_basis(self):
314
        builder = self.make_branch_builder('source')
315
        builder.start_series()
316
        self.addCleanup(builder.finish_series)
317
        builder.build_snapshot('A', [], [
318
            ('add', ('', 'root-id', 'directory', None)),
319
            ('add', ('a', 'a-id', 'file', 'content\n'))])
320
        builder.build_snapshot('B', ['A'], [
321
            ('modify', ('a-id', 'new content\nfor a\n')),
322
            ('add', ('b', 'b-id', 'file', 'b-content\n'))])
323
        builder.build_snapshot('C', ['A'], [
324
            ('add', ('c', 'c-id', 'file', 'c-content\n'))])
325
        b_c = self.make_branch('branch_with_c')
326
        b_c.pull(builder.get_branch(), stop_revision='C')
327
        b_b = self.make_branch('branch_with_b')
328
        b_b.pull(builder.get_branch(), stop_revision='B')
329
        # This is reproducing some of what 'switch' does, just to isolate the
330
        # set_parent_trees() step.
331
        wt = b_b.create_checkout('tree', lightweight=True)
332
        fmt = wt.bzrdir.find_branch_format()
333
        fmt.set_reference(wt.bzrdir, None, b_c)
334
        # Re-open with the new reference
335
        wt = wt.bzrdir.open_workingtree()
336
        wt.set_parent_trees([('C', b_c.repository.revision_tree('C'))])
337
        self.assertEqual(None, wt.basis_tree().path2id('b'))
338
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
339
    def test_new_dirstate_on_new_lock(self):
340
        # until we have detection for when a dirstate can be reused, we
341
        # want to reparse dirstate on every new lock.
342
        known_dirstates = set()
343
        def lock_and_compare_all_current_dirstate(tree, lock_method):
344
            getattr(tree, lock_method)()
345
            state = tree.current_dirstate()
346
            self.assertFalse(state in known_dirstates)
347
            known_dirstates.add(state)
348
            tree.unlock()
349
        tree = self.make_workingtree()
350
        # lock twice with each type to prevent silly per-lock-type bugs.
351
        # each lock and compare looks for a unique state object.
352
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
353
        lock_and_compare_all_current_dirstate(tree, 'lock_read')
354
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
355
        lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
356
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
357
        lock_and_compare_all_current_dirstate(tree, 'lock_write')
358
2255.2.122 by Robert Collins
Alter intertree implementation tests to let dirstate inter-trees be correctly parameterised.
359
    def test_constructing_invalid_interdirstate_raises(self):
360
        tree = self.make_workingtree()
361
        rev_id = tree.commit('first post')
362
        rev_id2 = tree.commit('second post')
363
        rev_tree = tree.branch.repository.revision_tree(rev_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
364
        # Exception is not a great thing to raise, but this test is
365
        # very short, and code is used to sanity check other tests, so
2255.2.122 by Robert Collins
Alter intertree implementation tests to let dirstate inter-trees be correctly parameterised.
366
        # a full error object is YAGNI.
367
        self.assertRaises(
368
            Exception, workingtree_4.InterDirStateTree, rev_tree, tree)
369
        self.assertRaises(
370
            Exception, workingtree_4.InterDirStateTree, tree, rev_tree)
371
2255.2.121 by John Arbash Meinel
split out the WorkingTreeFormat4 tests into a separate test file
372
    def test_revtree_to_revtree_not_interdirstate(self):
373
        # we should not get a dirstate optimiser for two repository sourced
374
        # revtrees. we can't prove a negative, so we dont do exhaustive tests
375
        # of all formats; though that could be written in the future it doesn't
376
        # seem well worth it.
377
        tree = self.make_workingtree()
378
        rev_id = tree.commit('first post')
379
        rev_id2 = tree.commit('second post')
380
        rev_tree = tree.branch.repository.revision_tree(rev_id)
381
        rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
382
        optimiser = InterTree.get(rev_tree, rev_tree2)
383
        self.assertIsInstance(optimiser, InterTree)
384
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
385
        optimiser = InterTree.get(rev_tree2, rev_tree)
386
        self.assertIsInstance(optimiser, InterTree)
387
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
388
389
    def test_revtree_not_in_dirstate_to_dirstate_not_interdirstate(self):
390
        # we should not get a dirstate optimiser when the revision id for of
391
        # the source is not in the dirstate of the target.
392
        tree = self.make_workingtree()
393
        rev_id = tree.commit('first post')
394
        rev_id2 = tree.commit('second post')
395
        rev_tree = tree.branch.repository.revision_tree(rev_id)
396
        tree.lock_read()
397
        optimiser = InterTree.get(rev_tree, tree)
398
        self.assertIsInstance(optimiser, InterTree)
399
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
400
        optimiser = InterTree.get(tree, rev_tree)
401
        self.assertIsInstance(optimiser, InterTree)
402
        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
403
        tree.unlock()
404
405
    def test_empty_basis_to_dirstate_tree(self):
406
        # we should get a InterDirStateTree for doing
407
        # 'changes_from' from the first basis dirstate revision tree to a
408
        # WorkingTree4.
409
        tree = self.make_workingtree()
410
        tree.lock_read()
411
        basis_tree = tree.basis_tree()
412
        basis_tree.lock_read()
413
        optimiser = InterTree.get(basis_tree, tree)
414
        tree.unlock()
415
        basis_tree.unlock()
416
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
417
418
    def test_nonempty_basis_to_dirstate_tree(self):
419
        # we should get a InterDirStateTree for doing
420
        # 'changes_from' from a non-null basis dirstate revision tree to a
421
        # WorkingTree4.
422
        tree = self.make_workingtree()
423
        tree.commit('first post')
424
        tree.lock_read()
425
        basis_tree = tree.basis_tree()
426
        basis_tree.lock_read()
427
        optimiser = InterTree.get(basis_tree, tree)
428
        tree.unlock()
429
        basis_tree.unlock()
430
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
431
432
    def test_empty_basis_revtree_to_dirstate_tree(self):
433
        # we should get a InterDirStateTree for doing
434
        # 'changes_from' from an empty repository based rev tree to a
435
        # WorkingTree4.
436
        tree = self.make_workingtree()
437
        tree.lock_read()
438
        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
439
        basis_tree.lock_read()
440
        optimiser = InterTree.get(basis_tree, tree)
441
        tree.unlock()
442
        basis_tree.unlock()
443
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
444
445
    def test_nonempty_basis_revtree_to_dirstate_tree(self):
446
        # we should get a InterDirStateTree for doing
447
        # 'changes_from' from a non-null repository based rev tree to a
448
        # WorkingTree4.
449
        tree = self.make_workingtree()
450
        tree.commit('first post')
451
        tree.lock_read()
452
        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
453
        basis_tree.lock_read()
454
        optimiser = InterTree.get(basis_tree, tree)
455
        tree.unlock()
456
        basis_tree.unlock()
457
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
458
459
    def test_tree_to_basis_in_other_tree(self):
460
        # we should get a InterDirStateTree when
461
        # the source revid is in the dirstate object of the target and
462
        # the dirstates are different. This is largely covered by testing
463
        # with repository revtrees, so is just for extra confidence.
464
        tree = self.make_workingtree('a')
465
        tree.commit('first post')
466
        tree2 = self.make_workingtree('b')
467
        tree2.pull(tree.branch)
468
        basis_tree = tree.basis_tree()
469
        tree2.lock_read()
470
        basis_tree.lock_read()
471
        optimiser = InterTree.get(basis_tree, tree2)
472
        tree2.unlock()
473
        basis_tree.unlock()
474
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
475
476
    def test_merged_revtree_to_tree(self):
477
        # we should get a InterDirStateTree when
478
        # the source tree is a merged tree present in the dirstate of target.
479
        tree = self.make_workingtree('a')
480
        tree.commit('first post')
481
        tree.commit('tree 1 commit 2')
482
        tree2 = self.make_workingtree('b')
483
        tree2.pull(tree.branch)
484
        tree2.commit('tree 2 commit 2')
485
        tree.merge_from_branch(tree2.branch)
486
        second_parent_tree = tree.revision_tree(tree.get_parent_ids()[1])
487
        second_parent_tree.lock_read()
488
        tree.lock_read()
489
        optimiser = InterTree.get(second_parent_tree, tree)
490
        tree.unlock()
491
        second_parent_tree.unlock()
492
        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
493
494
    def test_id2path(self):
495
        tree = self.make_workingtree('tree')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
496
        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
497
        tree.add(['a'], ['a-id'])
498
        self.assertEqual(u'a', tree.id2path('a-id'))
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
499
        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
500
        tree.commit('a')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
501
        tree.add(['b'], ['b-id'])
2255.2.144 by John Arbash Meinel
Simplify update_minimal a bit more, by making id_index a
502
2321.1.2 by Robert Collins
Skip new tests that depend on unicode file paths.
503
        try:
2825.6.1 by Robert Collins
* ``WorkingTree.rename_one`` will now raise an error if normalisation of the
504
            new_path = u'b\u03bcrry'
505
            tree.rename_one('a', new_path)
2321.1.2 by Robert Collins
Skip new tests that depend on unicode file paths.
506
        except UnicodeEncodeError:
507
            # support running the test on non-unicode platforms
508
            new_path = 'c'
2825.6.1 by Robert Collins
* ``WorkingTree.rename_one`` will now raise an error if normalisation of the
509
            tree.rename_one('a', new_path)
2321.1.2 by Robert Collins
Skip new tests that depend on unicode file paths.
510
        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
511
        tree.commit(u'b\xb5rry')
512
        tree.unversion(['a-id'])
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
513
        self.assertRaises(errors.NoSuchId, tree.id2path, 'a-id')
2255.2.147 by John Arbash Meinel
Move fast id => path lookups down into DirState
514
        self.assertEqual('b', tree.id2path('b-id'))
2255.11.5 by Martin Pool
Tree.id2path should raise NoSuchId, not return None.
515
        self.assertRaises(errors.NoSuchId, tree.id2path, 'c-id')
2255.2.166 by Martin Pool
(broken) Add Tree.get_root_id() & test
516
517
    def test_unique_root_id_per_tree(self):
518
        # each time you initialize a new tree, it gets a different root id
6437.14.2 by Jelmer Vernooij
Run subtree tests with development-subtree rather than deprecated dirstate-with-subtree.
519
        format_name = 'development-subtree'
2255.2.166 by Martin Pool
(broken) Add Tree.get_root_id() & test
520
        tree1 = self.make_branch_and_tree('tree1',
521
            format=format_name)
522
        tree2 = self.make_branch_and_tree('tree2',
523
            format=format_name)
524
        self.assertNotEqual(tree1.get_root_id(), tree2.get_root_id())
525
        # when you branch, it inherits the same root id
526
        rev1 = tree1.commit('first post')
527
        tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
528
        self.assertEqual(tree3.get_root_id(), tree1.get_root_id())
529
2255.11.2 by Martin Pool
Add more dirstate root-id-changing tests
530
    def test_set_root_id(self):
531
        # similar to some code that fails in the dirstate-plus-subtree branch
532
        # -- setting the root id while adding a parent seems to scramble the
533
        # dirstate invariants. -- mbp 20070303
534
        def validate():
535
            wt.lock_read()
536
            try:
537
                wt.current_dirstate()._validate()
538
            finally:
539
                wt.unlock()
540
        wt = self.make_workingtree('tree')
541
        wt.set_root_id('TREE-ROOTID')
542
        validate()
543
        wt.commit('somenthing')
544
        validate()
545
        # now switch and commit again
546
        wt.set_root_id('tree-rootid')
547
        validate()
548
        wt.commit('again')
549
        validate()
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
550
1551.15.6 by Aaron Bentley
Use ROOT_ID when the repository supports old clients (Bug #107168)
551
    def test_default_root_id(self):
552
        tree = self.make_branch_and_tree('tag', format='dirstate-tags')
553
        self.assertEqual(inventory.ROOT_ID, tree.get_root_id())
554
        tree = self.make_branch_and_tree('subtree',
6437.14.2 by Jelmer Vernooij
Run subtree tests with development-subtree rather than deprecated dirstate-with-subtree.
555
                                         format='development-subtree')
1551.15.6 by Aaron Bentley
Use ROOT_ID when the repository supports old clients (Bug #107168)
556
        self.assertNotEqual(inventory.ROOT_ID, tree.get_root_id())
557
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
558
    def test_non_subtree_with_nested_trees(self):
559
        # prior to dirstate, st/diff/commit ignored nested trees.
6437.14.2 by Jelmer Vernooij
Run subtree tests with development-subtree rather than deprecated dirstate-with-subtree.
560
        # dirstate, as opposed to development-subtree, should
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
561
        # behave the same way.
562
        tree = self.make_branch_and_tree('.', format='dirstate')
563
        self.assertFalse(tree.supports_tree_reference())
564
        self.build_tree(['dir/'])
565
        # for testing easily.
566
        tree.set_root_id('root')
567
        tree.add(['dir'], ['dir-id'])
568
        subtree = self.make_branch_and_tree('dir')
569
        # the most primitive operation: kind
570
        self.assertEqual('directory', tree.kind('dir-id'))
4570.2.8 by Robert Collins
Adjust WorkingTree4 specific test to deal with iter_changes reporting required directories.
571
        # a diff against the basis should give us a directory and the root (as
572
        # the root is new too).
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
573
        tree.lock_read()
574
        expected = [('dir-id',
575
            (None, u'dir'),
576
            True,
577
            (False, True),
578
            (None, 'root'),
579
            (None, u'dir'),
580
            (None, 'directory'),
4570.2.8 by Robert Collins
Adjust WorkingTree4 specific test to deal with iter_changes reporting required directories.
581
            (None, False)),
582
            ('root', (None, u''), True, (False, True), (None, None),
583
            (None, u''), (None, 'directory'), (None, 0))]
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
584
        self.assertEqual(expected, list(tree.iter_changes(tree.basis_tree(),
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
585
            specific_files=['dir'])))
586
        tree.unlock()
587
        # do a commit, we want to trigger the dirstate fast-path too
588
        tree.commit('first post')
589
        # change the path for the subdir, which will trigger getting all
590
        # its data:
591
        os.rename('dir', 'also-dir')
592
        # now the diff will use the fast path
593
        tree.lock_read()
594
        expected = [('dir-id',
595
            (u'dir', u'dir'),
596
            True,
597
            (True, True),
598
            ('root', 'root'),
599
            ('dir', 'dir'),
600
            ('directory', None),
601
            (False, False))]
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
602
        self.assertEqual(expected, list(tree.iter_changes(tree.basis_tree())))
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
603
        tree.unlock()
604
605
    def test_with_subtree_supports_tree_references(self):
6437.14.2 by Jelmer Vernooij
Run subtree tests with development-subtree rather than deprecated dirstate-with-subtree.
606
        # development-subtree should support tree-references.
607
        tree = self.make_branch_and_tree('.', format='development-subtree')
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
608
        self.assertTrue(tree.supports_tree_reference())
609
        # having checked this is on, the tree interface, and intertree
610
        # interface tests, will proceed to test the subtree support of
611
        # workingtree_4.
2466.4.1 by John Arbash Meinel
Add a (failing) test that exposes how _iter_changes is accidentally walking into unversioned directories.
612
613
    def test_iter_changes_ignores_unversioned_dirs(self):
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
614
        """iter_changes should not descend into unversioned directories."""
2466.4.1 by John Arbash Meinel
Add a (failing) test that exposes how _iter_changes is accidentally walking into unversioned directories.
615
        tree = self.make_branch_and_tree('.', format='dirstate')
616
        # We have an unversioned directory at the root, a versioned one with
617
        # other versioned files and an unversioned directory, and another
618
        # versioned dir with nothing but an unversioned directory.
619
        self.build_tree(['unversioned/',
620
                         'unversioned/a',
621
                         'unversioned/b/',
622
                         'versioned/',
623
                         'versioned/unversioned/',
624
                         'versioned/unversioned/a',
625
                         'versioned/unversioned/b/',
626
                         'versioned2/',
627
                         'versioned2/a',
628
                         'versioned2/unversioned/',
629
                         'versioned2/unversioned/a',
630
                         'versioned2/unversioned/b/',
631
                        ])
632
        tree.add(['versioned', 'versioned2', 'versioned2/a'])
633
        tree.commit('one', rev_id='rev-1')
634
        # Trap osutils._walkdirs_utf8 to spy on what dirs have been accessed.
635
        returned = []
636
        def walkdirs_spy(*args, **kwargs):
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
637
            for val in orig(*args, **kwargs):
2466.4.1 by John Arbash Meinel
Add a (failing) test that exposes how _iter_changes is accidentally walking into unversioned directories.
638
                returned.append(val[0][0])
639
                yield val
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
640
        orig = self.overrideAttr(osutils, '_walkdirs_utf8', walkdirs_spy)
2466.4.1 by John Arbash Meinel
Add a (failing) test that exposes how _iter_changes is accidentally walking into unversioned directories.
641
642
        basis = tree.basis_tree()
643
        tree.lock_read()
644
        self.addCleanup(tree.unlock)
645
        basis.lock_read()
646
        self.addCleanup(basis.unlock)
2466.4.2 by John Arbash Meinel
Clean up the (failing) test so that the last thing
647
        changes = [c[1] for c in
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
648
                   tree.iter_changes(basis, want_unversioned=True)]
2466.4.2 by John Arbash Meinel
Clean up the (failing) test so that the last thing
649
        self.assertEqual([(None, 'unversioned'),
650
                          (None, 'versioned/unversioned'),
651
                          (None, 'versioned2/unversioned'),
652
                         ], changes)
653
        self.assertEqual(['', 'versioned', 'versioned2'], returned)
654
        del returned[:] # reset
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
655
        changes = [c[1] for c in tree.iter_changes(basis)]
2466.4.2 by John Arbash Meinel
Clean up the (failing) test so that the last thing
656
        self.assertEqual([], changes)
657
        self.assertEqual(['', 'versioned', 'versioned2'], returned)
3207.2.1 by jameinel
Add a test that _iter_changes raises a clearer error when we encounter an invalid rename.
658
4496.2.1 by Ian Clatworthy
(igc) Improve paths are not versioned reporting (Benoît PIERRE)
659
    def test_iter_changes_unversioned_error(self):
660
        """ Check if a PathsNotVersionedError is correctly raised and the
661
            paths list contains all unversioned entries only.
662
        """
663
        tree = self.make_branch_and_tree('tree')
664
        self.build_tree_contents([('tree/bar', '')])
665
        tree.add(['bar'], ['bar-id'])
666
        tree.lock_read()
667
        self.addCleanup(tree.unlock)
668
        tree_iter_changes = lambda files: [
669
            c for c in tree.iter_changes(tree.basis_tree(), specific_files=files,
670
                                         require_versioned=True)
671
        ]
672
        e = self.assertRaises(errors.PathsNotVersionedError,
673
                              tree_iter_changes, ['bar', 'foo'])
674
        self.assertEqual(e.paths, ['foo'])
675
6345.1.2 by Martin Packman
Add targetted working tree tests for places that raise PathsNotVersionedError
676
    def test_iter_changes_unversioned_non_ascii(self):
677
        """Unversioned non-ascii paths should be reported as unicode"""
678
        self.requireFeature(features.UnicodeFilenameFeature)
679
        tree = self.make_branch_and_tree('.')
680
        self.build_tree_contents([('f', '')])
681
        tree.add(['f'], ['f-id'])
682
        def tree_iter_changes(tree, files):
683
            return list(tree.iter_changes(tree.basis_tree(),
684
                specific_files=files, require_versioned=True))
685
        tree.lock_read()
686
        self.addCleanup(tree.unlock)
687
        e = self.assertRaises(errors.PathsNotVersionedError,
688
            tree_iter_changes, tree, [u'\xa7', u'\u03c0'])
689
        self.assertEqual(e.paths, [u'\xa7', u'\u03c0'])
690
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
691
    def get_tree_with_cachable_file_foo(self):
692
        tree = self.make_branch_and_tree('.')
5755.1.1 by John Arbash Meinel
Change WT._observed_sha1 to also update st.st_size.
693
        tree.lock_write()
694
        self.addCleanup(tree.unlock)
695
        self.build_tree_contents([('foo', 'a bit of content for foo\n')])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
696
        tree.add(['foo'], ['foo-id'])
5755.1.1 by John Arbash Meinel
Change WT._observed_sha1 to also update st.st_size.
697
        tree.current_dirstate()._cutoff_time = time.time() + 60
3207.2.1 by jameinel
Add a test that _iter_changes raises a clearer error when we encounter an invalid rename.
698
        return tree
699
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
700
    def test_commit_updates_hash_cache(self):
701
        tree = self.get_tree_with_cachable_file_foo()
702
        revid = tree.commit('a commit')
703
        # tree's dirstate should now have a valid stat entry for foo.
704
        entry = tree._get_entry(path='foo')
705
        expected_sha1 = osutils.sha_file_by_name('foo')
706
        self.assertEqual(expected_sha1, entry[1][0][1])
5755.1.2 by John Arbash Meinel
use soft constants rather than '25'
707
        self.assertEqual(len('a bit of content for foo\n'), entry[1][0][2])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
708
709
    def test_observed_sha1_cachable(self):
710
        tree = self.get_tree_with_cachable_file_foo()
711
        expected_sha1 = osutils.sha_file_by_name('foo')
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
712
        statvalue = os.lstat("foo")
5755.1.1 by John Arbash Meinel
Change WT._observed_sha1 to also update st.st_size.
713
        tree._observed_sha1("foo-id", "foo", (expected_sha1, statvalue))
714
        entry = tree._get_entry(path="foo")
715
        entry_state = entry[1][0]
716
        self.assertEqual(expected_sha1, entry_state[1])
5755.1.2 by John Arbash Meinel
use soft constants rather than '25'
717
        self.assertEqual(statvalue.st_size, entry_state[2])
5755.1.1 by John Arbash Meinel
Change WT._observed_sha1 to also update st.st_size.
718
        tree.unlock()
719
        tree.lock_read()
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
720
        tree = tree.bzrdir.open_workingtree()
721
        tree.lock_read()
3207.2.1 by jameinel
Add a test that _iter_changes raises a clearer error when we encounter an invalid rename.
722
        self.addCleanup(tree.unlock)
5755.1.1 by John Arbash Meinel
Change WT._observed_sha1 to also update st.st_size.
723
        entry = tree._get_entry(path="foo")
724
        entry_state = entry[1][0]
725
        self.assertEqual(expected_sha1, entry_state[1])
5755.1.2 by John Arbash Meinel
use soft constants rather than '25'
726
        self.assertEqual(statvalue.st_size, entry_state[2])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
727
728
    def test_observed_sha1_new_file(self):
729
        tree = self.make_branch_and_tree('.')
730
        self.build_tree(['foo'])
731
        tree.add(['foo'], ['foo-id'])
3207.2.2 by John Arbash Meinel
Fix bug #187169, when an invalid delta is supplied to update_basis_by_delta
732
        tree.lock_read()
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
733
        try:
734
            current_sha1 = tree._get_entry(path="foo")[1][0][1]
735
        finally:
736
            tree.unlock()
737
        tree.lock_write()
738
        try:
739
            tree._observed_sha1("foo-id", "foo",
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
740
                (osutils.sha_file_by_name('foo'), os.lstat("foo")))
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
741
            # Must not have changed
742
            self.assertEqual(current_sha1,
743
                tree._get_entry(path="foo")[1][0][1])
744
        finally:
745
            tree.unlock()
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
746
747
    def test_get_file_with_stat_id_only(self):
748
        # Explicit test to ensure we get a lstat value from WT4 trees.
749
        tree = self.make_branch_and_tree('.')
750
        self.build_tree(['foo'])
751
        tree.add(['foo'], ['foo-id'])
752
        tree.lock_read()
753
        self.addCleanup(tree.unlock)
754
        file_obj, statvalue = tree.get_file_with_stat('foo-id')
755
        expected = os.lstat('foo')
4807.2.2 by John Arbash Meinel
Move all the stat comparison and platform checkning code to assertEqualStat.
756
        self.assertEqualStat(expected, statvalue)
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
757
        self.assertEqual(["contents of foo\n"], file_obj.readlines())
758
759
760
class TestCorruptDirstate(TestCaseWithTransport):
761
    """Tests for how we handle when the dirstate has been corrupted."""
762
763
    def create_wt4(self):
764
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
765
        control.create_repository()
766
        control.create_branch()
767
        tree = workingtree_4.WorkingTreeFormat4().initialize(control)
768
        return tree
769
770
    def test_invalid_rename(self):
771
        tree = self.create_wt4()
772
        # Create a corrupted dirstate
773
        tree.lock_write()
774
        try:
4285.2.1 by Vincent Ladeuil
Cleanup test imports and use features to better track skipped tests.
775
            # We need a parent, or we always compare with NULL
776
            tree.commit('init')
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
777
            state = tree.current_dirstate()
778
            state._read_dirblocks_if_needed()
779
            # Now add in an invalid entry, a rename with a dangling pointer
780
            state._dirblocks[1][1].append((('', 'foo', 'foo-id'),
781
                                            [('f', '', 0, False, ''),
782
                                             ('r', 'bar', 0 , False, '')]))
783
            self.assertListRaises(errors.CorruptDirstate,
784
                                  tree.iter_changes, tree.basis_tree())
785
        finally:
786
            tree.unlock()
787
788
    def get_simple_dirblocks(self, state):
789
        """Extract the simple information from the DirState.
790
791
        This returns the dirblocks, only with the sha1sum and stat details
792
        filtered out.
793
        """
794
        simple_blocks = []
795
        for block in state._dirblocks:
796
            simple_block = (block[0], [])
797
            for entry in block[1]:
798
                # Include the key for each entry, and for each parent include
799
                # just the minikind, so we know if it was
800
                # present/absent/renamed/etc
801
                simple_block[1].append((entry[0], [i[0] for i in entry[1]]))
802
            simple_blocks.append(simple_block)
803
        return simple_blocks
804
805
    def test_update_basis_with_invalid_delta(self):
806
        """When given an invalid delta, it should abort, and not be saved."""
807
        self.build_tree(['dir/', 'dir/file'])
808
        tree = self.create_wt4()
809
        tree.lock_write()
810
        self.addCleanup(tree.unlock)
811
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
812
        first_revision_id = tree.commit('init')
813
814
        root_id = tree.path2id('')
815
        state = tree.current_dirstate()
816
        state._read_dirblocks_if_needed()
817
        self.assertEqual([
818
            ('', [(('', '', root_id), ['d', 'd'])]),
819
            ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
820
            ('dir', [(('dir', 'file', 'file-id'), ['f', 'f'])]),
821
        ],  self.get_simple_dirblocks(state))
822
823
        tree.remove(['dir/file'])
824
        self.assertEqual([
825
            ('', [(('', '', root_id), ['d', 'd'])]),
826
            ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
827
            ('dir', [(('dir', 'file', 'file-id'), ['a', 'f'])]),
828
        ],  self.get_simple_dirblocks(state))
829
        # Make sure the removal is written to disk
830
        tree.flush()
831
832
        # self.assertRaises(Exception, tree.update_basis_by_delta,
833
        new_dir = inventory.InventoryDirectory('dir-id', 'new-dir', root_id)
834
        new_dir.revision = 'new-revision-id'
835
        new_file = inventory.InventoryFile('file-id', 'new-file', root_id)
836
        new_file.revision = 'new-revision-id'
837
        self.assertRaises(errors.InconsistentDelta,
838
            tree.update_basis_by_delta, 'new-revision-id',
839
            [('dir', 'new-dir', 'dir-id', new_dir),
840
             ('dir/file', 'new-dir/new-file', 'file-id', new_file),
841
            ])
842
        del state
843
844
        # Now when we re-read the file it should not have been modified
845
        tree.unlock()
846
        tree.lock_read()
847
        self.assertEqual(first_revision_id, tree.last_revision())
848
        state = tree.current_dirstate()
849
        state._read_dirblocks_if_needed()
850
        self.assertEqual([
851
            ('', [(('', '', root_id), ['d', 'd'])]),
852
            ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
853
            ('dir', [(('dir', 'file', 'file-id'), ['a', 'f'])]),
854
        ],  self.get_simple_dirblocks(state))
4634.156.1 by Vincent Ladeuil
Don't traceback when unversioning a directory.
855
856
857
class TestInventoryCoherency(TestCaseWithTransport):
858
859
    def test_inventory_is_synced_when_unversioning_a_dir(self):
860
        """Unversioning the root of a subtree unversions the entire subtree."""
861
        tree = self.make_branch_and_tree('.')
862
        self.build_tree(['a/', 'a/b', 'c/'])
863
        tree.add(['a', 'a/b', 'c'], ['a-id', 'b-id', 'c-id'])
864
        # within a lock unversion should take effect
865
        tree.lock_write()
866
        self.addCleanup(tree.unlock)
867
        # Force access to the in memory inventory to trigger bug #494221: try
868
        # maintaining the in-memory inventory
6405.2.10 by Jelmer Vernooij
Fix more tests.
869
        inv = tree.root_inventory
4634.156.2 by Vincent Ladeuil
Ensure the entries are removed from the inventory
870
        self.assertTrue(inv.has_id('a-id'))
871
        self.assertTrue(inv.has_id('b-id'))
4634.156.1 by Vincent Ladeuil
Don't traceback when unversioning a directory.
872
        tree.unversion(['a-id', 'b-id'])
4634.156.2 by Vincent Ladeuil
Ensure the entries are removed from the inventory
873
        self.assertFalse(inv.has_id('a-id'))
874
        self.assertFalse(inv.has_id('b-id'))