/brz/remove-bazaar

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