/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) 2006-2012, 2016 Canonical Ltd
1740.3.1 by Jelmer Vernooij
Introduce and use CommitBuilder objects.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1740.3.1 by Jelmer Vernooij
Introduce and use CommitBuilder objects.
16
17
"""Tests for repository commit builder."""
18
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
19
import os
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
20
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
21
from breezy import (
5609.31.2 by mbp at sourcefrog
Also turn off whoami inference in per_repository tests
22
    config,
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
23
    errors,
24
    osutils,
25
    repository,
3668.5.1 by Jelmer Vernooij
Use NULL_REVISION rather than None for Repository.revision_tree().
26
    revision as _mod_revision,
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
27
    tests,
28
    )
6670.4.3 by Jelmer Vernooij
Fix more imports.
29
from breezy.bzr import (
30
    inventory,
31
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
32
from breezy.tests import per_repository
33
from breezy.tests import (
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
34
    features,
35
    )
5010.2.24 by Vincent Ladeuil
Fix imports in per_repository/test_commit_builder.py.
36
37
38
class TestCommitBuilder(per_repository.TestCaseWithRepository):
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
39
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
40
    def test_get_commit_builder(self):
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
41
        branch = self.make_branch('.')
42
        branch.repository.lock_write()
43
        builder = branch.repository.get_commit_builder(
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
44
            branch, [], branch.get_config_stack())
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
45
        self.assertIsInstance(builder, repository.CommitBuilder)
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
46
        self.assertTrue(builder.random_revid)
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
47
        branch.repository.commit_write_group()
48
        branch.repository.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
49
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
50
    def test_finish_inventory_record_iter_changes(self):
51
        tree = self.make_branch_and_tree(".")
52
        tree.lock_write()
53
        try:
54
            builder = tree.branch.get_commit_builder([])
55
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
56
                list(builder.record_iter_changes(tree, tree.last_revision(),
57
                    tree.iter_changes(tree.basis_tree())))
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
58
                builder.finish_inventory()
59
            except:
60
                builder.abort()
61
                raise
62
            repo = tree.branch.repository
63
            repo.commit_write_group()
2617.6.8 by Robert Collins
Review feedback and documentation.
64
        finally:
65
            tree.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
66
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
67
    def test_abort_record_iter_changes(self):
68
        tree = self.make_branch_and_tree(".")
69
        tree.lock_write()
70
        try:
71
            builder = tree.branch.get_commit_builder([])
72
            try:
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
73
                basis = tree.basis_tree()
74
                last_rev = tree.last_revision()
75
                changes = tree.iter_changes(basis)
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
76
                list(builder.record_iter_changes(tree, last_rev, changes))
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
77
                builder.finish_inventory()
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
78
            finally:
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
79
                builder.abort()
80
        finally:
81
            tree.unlock()
82
5777.6.8 by Jelmer Vernooij
Add test for get_commit_builder(lossy=True).
83
    def test_commit_lossy(self):
84
        tree = self.make_branch_and_tree(".")
85
        tree.lock_write()
86
        try:
87
            builder = tree.branch.get_commit_builder([], lossy=True)
88
            list(builder.record_iter_changes(tree, tree.last_revision(),
89
                tree.iter_changes(tree.basis_tree())))
90
            builder.finish_inventory()
91
            rev_id = builder.commit('foo bar blah')
92
        finally:
93
            tree.unlock()
94
        rev = tree.branch.repository.get_revision(rev_id)
95
        self.assertEqual('foo bar blah', rev.message)
96
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
97
    def test_commit_message(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
98
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
99
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
100
        try:
101
            builder = tree.branch.get_commit_builder([])
5718.4.6 by Jelmer Vernooij
inline function only used once.
102
            list(builder.record_iter_changes(tree, tree.last_revision(),
103
                tree.iter_changes(tree.basis_tree())))
104
            builder.finish_inventory()
2617.6.8 by Robert Collins
Review feedback and documentation.
105
            rev_id = builder.commit('foo bar blah')
106
        finally:
107
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
108
        rev = tree.branch.repository.get_revision(rev_id)
109
        self.assertEqual('foo bar blah', rev.message)
110
6217.5.1 by Jelmer Vernooij
Add CommitBuilder.updates_branch.
111
    def test_updates_branch(self):
112
        tree = self.make_branch_and_tree(".")
113
        tree.lock_write()
114
        try:
115
            builder = tree.branch.get_commit_builder([])
116
            list(builder.record_iter_changes(tree, tree.last_revision(),
117
                tree.iter_changes(tree.basis_tree())))
118
            builder.finish_inventory()
119
            will_update_branch = builder.updates_branch
120
            rev_id = builder.commit('might update the branch')
121
        finally:
122
            tree.unlock()
123
        actually_updated_branch = (tree.branch.last_revision() == rev_id)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
124
        self.assertEqual(actually_updated_branch, will_update_branch)
6217.5.1 by Jelmer Vernooij
Add CommitBuilder.updates_branch.
125
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
126
    def test_commit_with_revision_id_record_iter_changes(self):
127
        tree = self.make_branch_and_tree(".")
128
        tree.lock_write()
129
        try:
130
            # use a unicode revision id to test more corner cases.
131
            # The repository layer is meant to handle this.
132
            revision_id = u'\xc8abc'.encode('utf8')
133
            try:
134
                try:
135
                    builder = tree.branch.get_commit_builder([],
136
                        revision_id=revision_id)
137
                except errors.NonAsciiRevisionId:
138
                    revision_id = 'abc'
139
                    builder = tree.branch.get_commit_builder([],
140
                        revision_id=revision_id)
6747.1.1 by Jelmer Vernooij
Remove unnecessary ExcludesUnsupported exception.
141
            except repository.CannotSetRevisionId:
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
142
                # This format doesn't support supplied revision ids
143
                return
144
            self.assertFalse(builder.random_revid)
145
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
146
                list(builder.record_iter_changes(tree, tree.last_revision(),
147
                    tree.iter_changes(tree.basis_tree())))
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
148
                builder.finish_inventory()
149
            except:
150
                builder.abort()
151
                raise
152
            self.assertEqual(revision_id, builder.commit('foo bar'))
153
        finally:
154
            tree.unlock()
155
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
156
        # the revision id must be set on the inventory when saving it. This
157
        # does not precisely test that - a repository that wants to can add it
158
        # on deserialisation, but thats all the current contract guarantees
159
        # anyway.
160
        self.assertEqual(revision_id,
6113.1.4 by Jelmer Vernooij
Use revision_tree for testing, it's always present.
161
            tree.branch.repository.revision_tree(revision_id).get_revision_id())
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
162
5222.1.1 by Aaron Bentley
Refuse to commit trees with no root.
163
    def test_commit_without_root_errors(self):
164
        tree = self.make_branch_and_tree(".")
165
        tree.lock_write()
166
        try:
167
            builder = tree.branch.get_commit_builder([])
168
            def do_commit():
169
                try:
170
                    list(builder.record_iter_changes(
171
                        tree, tree.last_revision(), []))
172
                    builder.finish_inventory()
173
                except:
174
                    builder.abort()
175
                    raise
6202.2.1 by Jelmer Vernooij
Commit in commit builder test to prevent masking errors.
176
                else:
177
                    builder.commit("msg")
5222.1.1 by Aaron Bentley
Refuse to commit trees with no root.
178
            self.assertRaises(errors.RootMissing, do_commit)
179
        finally:
180
            tree.unlock()
181
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
182
    def test_commit_unchanged_root_record_iter_changes(self):
183
        tree = self.make_branch_and_tree(".")
184
        old_revision_id = tree.commit('')
185
        tree.lock_write()
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
186
        builder = tree.branch.get_commit_builder([old_revision_id])
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
187
        try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
188
            list(builder.record_iter_changes(tree, old_revision_id, []))
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
189
            # Regardless of repository root behaviour we should consider this a
190
            # pointless commit.
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
191
            self.assertFalse(builder.any_changes())
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
192
            builder.finish_inventory()
6362.5.5 by Jelmer Vernooij
Don't access revision tree before commit.
193
            builder.commit('')
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
194
            builder_tree = builder.revision_tree()
195
            new_root_id = builder_tree.get_root_id()
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
196
            new_root_revision = builder_tree.get_file_revision(u'')
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
197
            if tree.branch.repository.supports_rich_root():
198
                # We should not have seen a new root revision
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
199
                self.assertEqual(old_revision_id, new_root_revision)
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
200
            else:
201
                # We should see a new root revision
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
202
                self.assertNotEqual(old_revision_id, new_root_revision)
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
203
        finally:
204
            tree.unlock()
205
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
206
    def test_record_delete_record_iter_changes(self):
207
        tree = self.make_branch_and_tree(".")
208
        self.build_tree(["foo"])
6770.2.2 by Jelmer Vernooij
Avoid file ids.
209
        tree.add(["foo"])
210
        foo_id = tree.path2id('foo')
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
211
        rev_id = tree.commit("added foo")
212
        tree.lock_write()
213
        try:
214
            builder = tree.branch.get_commit_builder([rev_id])
215
            try:
6770.2.2 by Jelmer Vernooij
Avoid file ids.
216
                delete_change = (foo_id, ('foo', None), True, (True, False),
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
217
                    (tree.path2id(''), None), ('foo', None), ('file', None),
218
                    (False, None))
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
219
                list(builder.record_iter_changes(tree, rev_id,
220
                    [delete_change]))
6770.2.2 by Jelmer Vernooij
Avoid file ids.
221
                self.assertEqual(("foo", None, foo_id, None),
6437.12.2 by Jelmer Vernooij
Remove more uses of CommitBuilder._basis_delta.
222
                    builder.get_basis_delta()[0])
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
223
                self.assertTrue(builder.any_changes())
224
                builder.finish_inventory()
225
                rev_id2 = builder.commit('delete foo')
226
            except:
227
                builder.abort()
228
                raise
229
        finally:
230
            tree.unlock()
231
        rev_tree = builder.revision_tree()
232
        rev_tree.lock_read()
233
        self.addCleanup(rev_tree.unlock)
6913.3.5 by Jelmer Vernooij
Avoid calls to path2id.
234
        self.assertFalse(rev_tree.is_versioned('foo'))
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
235
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
236
    def test_revision_tree_record_iter_changes(self):
237
        tree = self.make_branch_and_tree(".")
238
        tree.lock_write()
239
        try:
240
            builder = tree.branch.get_commit_builder([])
241
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
242
                list(builder.record_iter_changes(tree,
243
                    _mod_revision.NULL_REVISION,
244
                    tree.iter_changes(tree.basis_tree())))
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
245
                builder.finish_inventory()
246
                rev_id = builder.commit('foo bar')
247
            except:
248
                builder.abort()
249
                raise
250
            rev_tree = builder.revision_tree()
251
            # Just a couple simple tests to ensure that it actually follows
252
            # the RevisionTree api.
253
            self.assertEqual(rev_id, rev_tree.get_revision_id())
6072.1.1 by Jelmer Vernooij
Various fixes for tests of foreign plugins.
254
            self.assertEqual((), tuple(rev_tree.get_parent_ids()))
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
255
        finally:
256
            tree.unlock()
257
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
258
    def test_root_entry_has_revision(self):
259
        # test the root revision created and put in the basis
260
        # has the right rev id.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
261
        # XXX: RBC 20081118 - this test is too big, it depends on the exact
262
        # behaviour of tree methods and so on; it should be written to the
263
        # commit builder interface directly.
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
264
        tree = self.make_branch_and_tree('.')
265
        rev_id = tree.commit('message')
266
        basis_tree = tree.basis_tree()
267
        basis_tree.lock_read()
268
        self.addCleanup(basis_tree.unlock)
5819.2.4 by Jelmer Vernooij
Avoid using inventory.
269
        self.assertEqual(rev_id,
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
270
            basis_tree.get_file_revision(u'', basis_tree.get_root_id()))
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
271
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
272
    def _get_revtrees(self, tree, revision_ids):
2592.3.214 by Robert Collins
Merge bzr.dev.
273
        tree.lock_read()
274
        try:
275
            trees = list(tree.branch.repository.revision_trees(revision_ids))
276
            for _tree in trees:
277
                _tree.lock_read()
278
                self.addCleanup(_tree.unlock)
279
            return trees
280
        finally:
281
            tree.unlock()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
282
283
    def test_last_modified_revision_after_commit_root_unchanged(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
284
        # commiting without changing the root does not change the
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
285
        # last modified except on non-rich-root-repositories.
286
        tree = self.make_branch_and_tree('.')
287
        rev1 = tree.commit('')
288
        rev2 = tree.commit('')
289
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
290
        self.assertEqual(rev1, tree1.get_file_revision(u''))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
291
        if tree.branch.repository.supports_rich_root():
6861.1.1 by Jelmer Vernooij
More foreign branch test fixes.
292
            self.assertEqual(rev1, tree2.get_file_revision(u''))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
293
        else:
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
294
            self.assertEqual(rev2, tree2.get_file_revision(u''))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
295
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
296
    def _add_commit_check_unchanged(self, tree, name):
6770.2.3 by Jelmer Vernooij
Fix file ids.
297
        tree.add([name])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
298
        self._commit_check_unchanged(tree, name, tree.path2id(name))
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
299
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
300
    def _commit_check_unchanged(self, tree, name, file_id):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
301
        rev1 = tree.commit('')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
302
        rev2 = self.mini_commit_record_iter_changes(tree, name, name, False, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
303
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
6913.3.5 by Jelmer Vernooij
Avoid calls to path2id.
304
        self.assertEqual(rev1, tree1.get_file_revision(name))
305
        self.assertEqual(rev1, tree2.get_file_revision(name))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
306
        expected_graph = {}
307
        expected_graph[(file_id, rev1)] = ()
308
        self.assertFileGraph(expected_graph, tree, (file_id, rev1))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
309
310
    def test_last_modified_revision_after_commit_dir_unchanged(self):
311
        # committing without changing a dir does not change the last modified.
312
        tree = self.make_branch_and_tree('.')
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
313
        if not tree.has_versioned_directories():
314
            raise tests.TestNotApplicable(
315
                    'Format does not support versioned directories')
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
316
        self.build_tree(['dir/'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
317
        self._add_commit_check_unchanged(tree, 'dir')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
318
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
319
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
320
        # committing without changing a dir does not change the last modified
321
        # of the dir even the dirs contents are changed.
322
        tree = self.make_branch_and_tree('.')
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
323
        self.build_tree(['dir/', 'dir/orig'])
324
        tree.add(['dir', 'dir/orig'])
6770.2.4 by Jelmer Vernooij
Fix remaining test.
325
        dir_id = tree.path2id('dir')
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
326
        rev1 = tree.commit('')
327
        self.build_tree(['dir/content'])
6770.2.4 by Jelmer Vernooij
Fix remaining test.
328
        tree.add(['dir/content'])
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
329
        rev2 = tree.commit('')
330
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
331
        self.assertEqual(rev1, tree1.get_file_revision('dir'))
6809.4.5 by Jelmer Vernooij
Swap arguments for get_file_*.
332
        self.assertEqual(rev1, tree2.get_file_revision('dir'))
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
333
        expected_graph = {(dir_id, rev1): ()}
6770.2.4 by Jelmer Vernooij
Fix remaining test.
334
        self.assertFileGraph(expected_graph, tree, (dir_id, rev1))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
335
336
    def test_last_modified_revision_after_commit_file_unchanged(self):
337
        # committing without changing a file does not change the last modified.
338
        tree = self.make_branch_and_tree('.')
339
        self.build_tree(['file'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
340
        self._add_commit_check_unchanged(tree, 'file')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
341
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
342
    def test_last_modified_revision_after_commit_link_unchanged(self):
343
        # committing without changing a link does not change the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
344
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
345
        tree = self.make_branch_and_tree('.')
346
        os.symlink('target', 'link')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
347
        self._add_commit_check_unchanged(tree, 'link')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
348
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
349
    def test_last_modified_revision_after_commit_reference_unchanged(self):
350
        # committing without changing a subtree does not change the last
351
        # modified.
352
        tree = self.make_branch_and_tree('.')
353
        subtree = self.make_reference('reference')
354
        try:
355
            tree.add_reference(subtree)
356
            self._commit_check_unchanged(tree, 'reference',
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
357
                subtree.get_root_id())
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
358
        except errors.UnsupportedOperation:
359
            return
360
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
361
    def _add_commit_renamed_check_changed(self, tree, name,
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
362
        expect_fs_hash=False):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
363
        def rename():
364
            tree.rename_one(name, 'new_' + name)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
365
        self._add_commit_change_check_changed(tree,
366
                (name, 'new_' + name), rename,
367
            expect_fs_hash=expect_fs_hash)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
368
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
369
    def _commit_renamed_check_changed(self, tree, name,
370
        expect_fs_hash=False):
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
371
        def rename():
372
            tree.rename_one(name, 'new_' + name)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
373
        self._commit_change_check_changed(tree, [name, 'new_' + name],
374
                rename, expect_fs_hash=expect_fs_hash)
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
375
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
376
    def test_last_modified_revision_after_rename_dir_changes(self):
377
        # renaming a dir changes the last modified.
378
        tree = self.make_branch_and_tree('.')
379
        self.build_tree(['dir/'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
380
        self._add_commit_renamed_check_changed(tree, 'dir')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
381
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
382
    def test_last_modified_revision_after_rename_file_changes(self):
383
        # renaming a file changes the last modified.
384
        tree = self.make_branch_and_tree('.')
385
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
386
        self._add_commit_renamed_check_changed(tree, 'file',
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
387
            expect_fs_hash=True)
3775.2.12 by Robert Collins
CommitBuilder.record_iter_changes handles renamed files.
388
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
389
    def test_last_modified_revision_after_rename_link_changes(self):
390
        # renaming a link changes the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
391
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
392
        tree = self.make_branch_and_tree('.')
393
        os.symlink('target', 'link')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
394
        self._add_commit_renamed_check_changed(tree, 'link')
3775.2.13 by Robert Collins
CommitBuilder.record_iter_changes handles renamed symlinks.
395
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
396
    def test_last_modified_revision_after_rename_ref_changes(self):
397
        # renaming a reference changes the last modified.
398
        tree = self.make_branch_and_tree('.')
399
        subtree = self.make_reference('reference')
400
        try:
401
            tree.add_reference(subtree)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
402
            self._commit_renamed_check_changed(tree, 'reference')
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
403
        except errors.UnsupportedOperation:
404
            return
405
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
406
    def _add_commit_reparent_check_changed(self, tree, name,
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
407
        expect_fs_hash=False):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
408
        self.build_tree(['newparent/'])
409
        tree.add(['newparent'])
410
        def reparent():
411
            tree.rename_one(name, 'newparent/new_' + name)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
412
        self._add_commit_change_check_changed(
413
                tree, (name, 'newparent/new_' + name), reparent,
414
            expect_fs_hash=expect_fs_hash)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
415
416
    def test_last_modified_revision_after_reparent_dir_changes(self):
417
        # reparenting a dir changes the last modified.
418
        tree = self.make_branch_and_tree('.')
419
        self.build_tree(['dir/'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
420
        self._add_commit_reparent_check_changed(tree, 'dir')
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
421
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
422
    def test_last_modified_revision_after_reparent_file_changes(self):
423
        # reparenting a file changes the last modified.
424
        tree = self.make_branch_and_tree('.')
425
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
426
        self._add_commit_reparent_check_changed(tree, 'file',
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
427
            expect_fs_hash=True)
3775.2.15 by Robert Collins
CommitBuilder.record_iter_changes handles reparented files.
428
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
429
    def test_last_modified_revision_after_reparent_link_changes(self):
430
        # reparenting a link changes the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
431
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
432
        tree = self.make_branch_and_tree('.')
433
        os.symlink('target', 'link')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
434
        self._add_commit_reparent_check_changed(tree, 'link')
3775.2.16 by Robert Collins
CommitBuilder.record_iter_changes handles reparented symlinks.
435
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
436
    def _add_commit_change_check_changed(self, tree, names, changer,
437
            expect_fs_hash=False):
438
        tree.add([names[0]])
439
        self.assertTrue(tree.is_versioned(names[0]))
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
440
        self._commit_change_check_changed(
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
441
            tree, names,
442
            changer, expect_fs_hash=expect_fs_hash)
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
443
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
444
    def _commit_change_check_changed(self, tree, names, changer,
445
                                     expect_fs_hash=False):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
446
        rev1 = tree.commit('')
447
        changer()
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
448
        rev2 = self.mini_commit_record_iter_changes(
449
                tree, names[0], names[1], expect_fs_hash=expect_fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
450
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
451
        self.assertEqual(rev1, tree1.get_file_revision(names[0]))
452
        self.assertEqual(rev2, tree2.get_file_revision(names[1]))
453
        file_id = tree1.path2id(names[0])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
454
        expected_graph = {}
455
        expected_graph[(file_id, rev1)] = ()
456
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
457
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
458
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
459
    def mini_commit_record_iter_changes(self, tree, name, new_name,
460
        records_version=True, delta_against_basis=True, expect_fs_hash=False):
461
        """Perform a miniature commit looking for record entry results.
462
463
        This version uses the record_iter_changes interface.
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
464
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
465
        :param tree: The tree to commit.
466
        :param name: The path in the basis tree of the tree being committed.
467
        :param new_name: The path in the tree being committed.
468
        :param records_version: True if the commit of new_name is expected to
469
            record a new version.
470
        :param delta_against_basis: True of the commit of new_name is expected
471
            to have a delta against the basis.
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
472
        :param expect_fs_hash: If true, looks for a fs hash output from
473
            record_iter_changes.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
474
        """
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
475
        with tree.lock_write():
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
476
            # mini manual commit here so we can check the return of
6700.2.1 by Jelmer Vernooij
Remove record_entry_contents tests and infrastructure.
477
            # record_iter_changes
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
478
            parent_ids = tree.get_parent_ids()
479
            builder = tree.branch.get_commit_builder(parent_ids)
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
480
            try:
481
                parent_tree = tree.basis_tree()
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
482
                with parent_tree.lock_read():
483
                    changes = list(tree.iter_changes(parent_tree))
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
484
                result = list(builder.record_iter_changes(tree, parent_ids[0],
485
                    changes))
486
                file_id = tree.path2id(new_name)
487
                self.assertIsNot(None, file_id)
488
                if expect_fs_hash:
489
                    tree_file_stat = tree.get_file_with_stat(new_name)
490
                    tree_file_stat[0].close()
491
                    self.assertLength(1, result)
492
                    result = result[0]
493
                    self.assertEqual(result[:2], (file_id, new_name))
494
                    self.assertEqual(result[2][0], tree.get_file_sha1(new_name))
495
                    self.assertEqualStat(result[2][1], tree_file_stat[1])
496
                else:
497
                    self.assertEqual([], result)
498
                builder.finish_inventory()
499
                if tree.branch.repository._format.supports_full_versioned_files:
500
                    inv_key = (builder._new_revision_id,)
501
                    inv_sha1 = tree.branch.repository.inventories.get_sha1s(
502
                                    [inv_key])[inv_key]
503
                    self.assertEqual(inv_sha1, builder.inv_sha1)
504
                rev2 = builder.commit('')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
505
            except BaseException:
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
506
                builder.abort()
507
                raise
6437.12.1 by Jelmer Vernooij
Use CommitBuilder.get_basis_delta rather than private CommitBuilder._basis_delta.
508
            delta = builder.get_basis_delta()
509
            delta_dict = dict((change[2], change) for change in delta)
510
            version_recorded = (file_id in delta_dict and
511
                delta_dict[file_id][3] is not None and
512
                delta_dict[file_id][3].revision == rev2)
513
            if records_version:
514
                self.assertTrue(version_recorded)
515
            else:
516
                self.assertFalse(version_recorded)
517
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
518
            revtree = builder.revision_tree()
6885.7.6 by Jelmer Vernooij
Fix tests.
519
            new_entry = revtree.iter_entries_by_dir(specific_files=[new_name]).next()[1]
6883.16.1 by Jelmer Vernooij
commit builder test improvements:
520
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
521
            if delta_against_basis:
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
522
                if tree.supports_rename_tracking() or name == new_name:
523
                    expected_delta = (name, new_name, file_id, new_entry)
524
                else:
525
                    expected_delta = (None, new_name, file_id, new_entry)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
526
                self.assertEqual(expected_delta, delta_dict[file_id])
527
            else:
528
                expected_delta = None
529
                self.assertFalse(version_recorded)
530
            tree.set_parent_ids([rev2])
531
        return rev2
532
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
533
    def assertFileGraph(self, expected_graph, tree, tip):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
534
        # all the changes that have occured should be in the ancestry
535
        # (closest to a public per-file graph API we have today)
536
        tree.lock_read()
537
        self.addCleanup(tree.unlock)
5815.5.9 by Jelmer Vernooij
Remove dependencies on texts.
538
        g = dict(tree.branch.repository.get_file_graph().iter_ancestry([tip]))
5010.2.24 by Vincent Ladeuil
Fix imports in per_repository/test_commit_builder.py.
539
        self.assertEqual(expected_graph, g)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
540
541
    def test_last_modified_revision_after_content_file_changes(self):
542
        # altering a file changes the last modified.
543
        tree = self.make_branch_and_tree('.')
544
        self.build_tree(['file'])
545
        def change_file():
6809.4.8 by Jelmer Vernooij
Fix some test failures.
546
            tree.put_file_bytes_non_atomic('file', 'new content')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
547
        self._add_commit_change_check_changed(tree, ('file', 'file'), change_file,
548
            expect_fs_hash=True)
3775.2.17 by Robert Collins
CommitBuilder.record_iter_changes handles changed files.
549
6700.2.1 by Jelmer Vernooij
Remove record_entry_contents tests and infrastructure.
550
    def _test_last_mod_rev_after_content_link_changes(
6770.2.1 by Jelmer Vernooij
Avoid setting file ids.
551
        self, link, target, newtarget):
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
552
        # changing a link changes the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
553
        self.requireFeature(features.SymlinkFeature)
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
554
        tree = self.make_branch_and_tree('.')
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
555
        os.symlink(target, link)
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
556
        def change_link():
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
557
            os.unlink(link)
558
            os.symlink(newtarget, link)
559
        self._add_commit_change_check_changed(
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
560
            tree, (link, link), change_link)
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
561
6700.2.1 by Jelmer Vernooij
Remove record_entry_contents tests and infrastructure.
562
    def test_last_modified_rev_after_content_link_changes(self):
563
        self._test_last_mod_rev_after_content_link_changes(
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
564
            'link', 'target', 'newtarget')
565
6700.2.1 by Jelmer Vernooij
Remove record_entry_contents tests and infrastructure.
566
    def test_last_modified_rev_after_content_unicode_link_changes(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
567
        self.requireFeature(features.UnicodeFilenameFeature)
6700.2.1 by Jelmer Vernooij
Remove record_entry_contents tests and infrastructure.
568
        self._test_last_mod_rev_after_content_link_changes(
6770.2.1 by Jelmer Vernooij
Avoid setting file ids.
569
            u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget')
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
570
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
571
    def _commit_sprout(self, tree, name):
6770.2.3 by Jelmer Vernooij
Fix file ids.
572
        tree.add([name])
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
573
        rev_id = tree.commit('')
6653.6.1 by Jelmer Vernooij
Rename a number of attributes from bzrdir to controldir.
574
        return rev_id, tree.controldir.sprout('t2').open_workingtree()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
575
576
    def _rename_in_tree(self, tree, name):
577
        tree.rename_one(name, 'new_' + name)
578
        return tree.commit('')
579
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
580
    def _commit_sprout_rename_merge(self, tree1, name, expect_fs_hash=False):
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
581
        """Do a rename in both trees."""
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
582
        rev1, tree2 = self._commit_sprout(tree1, name)
6770.2.3 by Jelmer Vernooij
Fix file ids.
583
        file_id = tree2.path2id(name)
6852.2.1 by Jelmer Vernooij
Allow formats to not support custom revision properties.
584
        self.assertIsNot(None, file_id)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
585
        # change both sides equally
586
        rev2 = self._rename_in_tree(tree1, name)
587
        rev3 = self._rename_in_tree(tree2, name)
588
        tree1.merge_from_branch(tree2.branch)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
589
        rev4 = self.mini_commit_record_iter_changes(tree1, 'new_' + name, 'new_' + name,
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
590
            expect_fs_hash=expect_fs_hash)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
591
        tree3, = self._get_revtrees(tree1, [rev4])
6913.3.5 by Jelmer Vernooij
Avoid calls to path2id.
592
        self.assertEqual(rev4, tree3.get_file_revision('new_' + name))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
593
        expected_graph = {}
594
        expected_graph[(file_id, rev1)] = ()
595
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
596
        expected_graph[(file_id, rev3)] = ((file_id, rev1),)
597
        expected_graph[(file_id, rev4)] = ((file_id, rev2), (file_id, rev3),)
598
        self.assertFileGraph(expected_graph, tree1, (file_id, rev4))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
599
600
    def test_last_modified_revision_after_merge_dir_changes(self):
601
        # merge a dir changes the last modified.
602
        tree1 = self.make_branch_and_tree('t1')
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
603
        if not tree1.has_versioned_directories():
604
            raise tests.TestNotApplicable(
605
                    'Format does not support versioned directories')
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
606
        self.build_tree(['t1/dir/'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
607
        self._commit_sprout_rename_merge(tree1, 'dir')
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
608
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
609
    def test_last_modified_revision_after_merge_file_changes(self):
610
        # merge a file changes the last modified.
611
        tree1 = self.make_branch_and_tree('t1')
612
        self.build_tree(['t1/file'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
613
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
3775.2.20 by Robert Collins
CommitBuilder.record_iter_changes handles merged files.
614
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
615
    def test_last_modified_revision_after_merge_link_changes(self):
616
        # merge a link changes the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
617
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
618
        tree1 = self.make_branch_and_tree('t1')
619
        os.symlink('target', 't1/link')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
620
        self._commit_sprout_rename_merge(tree1, 'link')
3775.2.21 by Robert Collins
CommitBuilder.record_iter_changes handles merged symlinks.
621
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
622
    def _commit_sprout_rename_merge_converged(self, tree1, name):
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
623
        # Make a merge which just incorporates a change from a branch:
624
        # The per-file graph is straight line, and no alteration occurs
625
        # in the inventory.
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
626
        # Part 1: change in the merged branch.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
627
        rev1, tree2 = self._commit_sprout(tree1, name)
6770.2.3 by Jelmer Vernooij
Fix file ids.
628
        file_id = tree2.path2id(name)
6852.2.1 by Jelmer Vernooij
Allow formats to not support custom revision properties.
629
        self.assertIsNot(None, file_id)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
630
        # change on the other side to merge back
631
        rev2 = self._rename_in_tree(tree2, name)
632
        tree1.merge_from_branch(tree2.branch)
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
633
        def _check_graph(in_tree, changed_in_tree):
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
634
            rev3 = self.mini_commit_record_iter_changes(
635
                    in_tree, name, 'new_' + name, False,
636
                    delta_against_basis=changed_in_tree)
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
637
            tree3, = self._get_revtrees(in_tree, [rev2])
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
638
            self.assertEqual(
639
                    rev2,
6913.3.5 by Jelmer Vernooij
Avoid calls to path2id.
640
                    tree3.get_file_revision('new_' + name, file_id))
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
641
            expected_graph = {}
642
            expected_graph[(file_id, rev1)] = ()
643
            expected_graph[(file_id, rev2)] = ((file_id, rev1),)
644
            self.assertFileGraph(expected_graph, in_tree, (file_id, rev2))
645
        _check_graph(tree1, True)
646
        # Part 2: change in the merged into branch - we use tree2 that has a
647
        # change to name, branch tree1 and give it an unrelated change, then
648
        # merge that to t2.
6653.6.1 by Jelmer Vernooij
Rename a number of attributes from bzrdir to controldir.
649
        other_tree = tree1.controldir.sprout('t3').open_workingtree()
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
650
        other_rev = other_tree.commit('')
651
        tree2.merge_from_branch(other_tree.branch)
652
        _check_graph(tree2, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
653
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
654
    def _commit_sprout_make_merge(self, tree1, make):
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
655
        # Make a merge which incorporates the addition of a new object to
656
        # another branch. The per-file graph shows no additional change
657
        # in the merge because its a straight line.
658
        rev1 = tree1.commit('')
6653.6.1 by Jelmer Vernooij
Rename a number of attributes from bzrdir to controldir.
659
        tree2 = tree1.controldir.sprout('t2').open_workingtree()
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
660
        # make and commit on the other side to merge back
661
        make('t2/name')
6770.2.2 by Jelmer Vernooij
Avoid file ids.
662
        tree2.add(['name'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
663
        self.assertTrue(tree2.is_versioned('name'))
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
664
        rev2 = tree2.commit('')
665
        tree1.merge_from_branch(tree2.branch)
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
666
        rev3 = self.mini_commit_record_iter_changes(tree1, None, 'name', False)
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
667
        tree3, = self._get_revtrees(tree1, [rev2])
668
        # in rev2, name should be only changed in rev2
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
669
        self.assertEqual(rev2, tree3.get_file_revision('name'))
670
        file_id = tree2.path2id('name')
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
671
        expected_graph = {}
672
        expected_graph[(file_id, rev2)] = ()
673
        self.assertFileGraph(expected_graph, tree1, (file_id, rev2))
674
675
    def test_last_modified_revision_after_converged_merge_dir_unchanged(self):
676
        # merge a dir that changed preserves the last modified.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
677
        tree1 = self.make_branch_and_tree('t1')
6883.14.2 by Jelmer Vernooij
Fix commitbuilder tests.
678
        if not tree1.has_versioned_directories():
679
            raise tests.TestNotApplicable(
680
                    'Format does not support versioned directories')
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
681
        self.build_tree(['t1/dir/'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
682
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
683
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
684
    def test_last_modified_revision_after_converged_merge_file_unchanged(self):
685
        # merge a file that changed preserves the last modified.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
686
        tree1 = self.make_branch_and_tree('t1')
687
        self.build_tree(['t1/file'])
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
688
        self._commit_sprout_rename_merge_converged(tree1, 'file')
3775.2.23 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch files.
689
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
690
    def test_last_modified_revision_after_converged_merge_link_unchanged(self):
691
        # merge a link that changed preserves the last modified.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
692
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
693
        tree1 = self.make_branch_and_tree('t1')
694
        os.symlink('target', 't1/link')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
695
        self._commit_sprout_rename_merge_converged(tree1, 'link')
3775.2.24 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch symlinks.
696
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
697
    def test_last_modified_revision_after_merge_new_dir_unchanged(self):
698
        # merge a new dir does not change the last modified.
699
        tree1 = self.make_branch_and_tree('t1')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
700
        self._commit_sprout_make_merge(tree1, self.make_dir)
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
701
702
    def test_last_modified_revision_after_merge_new_file_unchanged(self):
703
        # merge a new file does not change the last modified.
704
        tree1 = self.make_branch_and_tree('t1')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
705
        self._commit_sprout_make_merge(tree1, self.make_file)
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
706
707
    def test_last_modified_revision_after_merge_new_link_unchanged(self):
708
        # merge a new link does not change the last modified.
709
        tree1 = self.make_branch_and_tree('t1')
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
710
        self._commit_sprout_make_merge(tree1, self.make_link)
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
711
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
712
    def make_dir(self, name):
713
        self.build_tree([name + '/'])
714
715
    def make_file(self, name):
716
        self.build_tree([name])
717
718
    def make_link(self, name):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
719
        self.requireFeature(features.SymlinkFeature)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
720
        os.symlink('target', name)
721
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
722
    def make_reference(self, name):
723
        tree = self.make_branch_and_tree(name, format='1.9-rich-root')
724
        tree.commit('foo')
725
        return tree
726
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
727
    def _check_kind_change(self, make_before, make_after, expect_fs_hash=False):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
728
        tree = self.make_branch_and_tree('.')
729
        path = 'name'
730
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
731
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
732
        def change_kind():
6164.2.3 by Jelmer Vernooij
Use rmtree, since some vcses include control directories in every directory.
733
            if osutils.file_kind(path) == "directory":
734
                osutils.rmtree(path)
735
            else:
736
                osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
737
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
738
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
739
        self._add_commit_change_check_changed(tree, (path, path), change_kind,
740
            expect_fs_hash=expect_fs_hash)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
741
742
    def test_last_modified_dir_file(self):
6846.2.1 by Jelmer Vernooij
Skip a few tests that don't apply to brz-git.
743
        if not self.repository_format.supports_versioned_directories:
744
            # TODO(jelmer): Perhaps test this by creating a directory
745
            # with a file in it?
746
            raise tests.TestNotApplicable(
747
                'format does not support versioned directories')
6217.2.1 by Jelmer Vernooij
Allow tree implementations to not support kind changes.
748
        try:
749
            self._check_kind_change(self.make_dir, self.make_file,
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
750
                expect_fs_hash=True)
6217.2.1 by Jelmer Vernooij
Allow tree implementations to not support kind changes.
751
        except errors.UnsupportedKindChange:
752
            raise tests.TestSkipped(
753
                "tree does not support changing entry kind from "
754
                "directory to file")
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
755
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
756
    def test_last_modified_dir_link(self):
6846.2.1 by Jelmer Vernooij
Skip a few tests that don't apply to brz-git.
757
        if not self.repository_format.supports_versioned_directories:
758
            # TODO(jelmer): Perhaps test this by creating a directory
759
            # with a file in it?
760
            raise tests.TestNotApplicable(
761
                'format does not support versioned directories')
6217.2.1 by Jelmer Vernooij
Allow tree implementations to not support kind changes.
762
        try:
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
763
            self._check_kind_change(self.make_dir, self.make_link)
6217.2.1 by Jelmer Vernooij
Allow tree implementations to not support kind changes.
764
        except errors.UnsupportedKindChange:
765
            raise tests.TestSkipped(
766
                "tree does not support changing entry kind from "
767
                "directory to link")
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
768
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
769
    def test_last_modified_link_file(self):
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
770
        self._check_kind_change(self.make_link, self.make_file,
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
771
            expect_fs_hash=True)
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
772
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
773
    def test_last_modified_link_dir(self):
6846.2.1 by Jelmer Vernooij
Skip a few tests that don't apply to brz-git.
774
        if not self.repository_format.supports_versioned_directories:
775
            # TODO(jelmer): Perhaps test this by creating a directory
776
            # with a file in it?
777
            raise tests.TestNotApplicable(
778
                'format does not support versioned directories')
779
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
780
        self._check_kind_change(self.make_link, self.make_dir)
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
781
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
782
    def test_last_modified_file_dir(self):
6846.2.1 by Jelmer Vernooij
Skip a few tests that don't apply to brz-git.
783
        if not self.repository_format.supports_versioned_directories:
784
            # TODO(jelmer): Perhaps test this by creating a directory
785
            # with a file in it?
786
            raise tests.TestNotApplicable(
787
                'format does not support versioned directories')
788
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
789
        self._check_kind_change(self.make_file, self.make_dir)
3775.2.27 by Robert Collins
CommitBuilder.record_iter_changes handles files becoming directories and links.
790
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
791
    def test_last_modified_file_link(self):
6885.7.2 by Jelmer Vernooij
Simplify CommitBuilder tests, fix some tests for formats that don't support rename tracking.
792
        self._check_kind_change(self.make_file, self.make_link)
3775.2.28 by Robert Collins
Merge .dev.
793
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
794
    def test_get_commit_builder_with_invalid_revprops(self):
795
        branch = self.make_branch('.')
796
        branch.repository.lock_write()
797
        self.addCleanup(branch.repository.unlock)
798
        self.assertRaises(ValueError, branch.repository.get_commit_builder,
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
799
            branch, [], branch.get_config_stack(),
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
800
            revprops={'invalid': u'property\rwith\r\ninvalid chars'})
801
802
    def test_commit_builder_commit_with_invalid_message(self):
803
        branch = self.make_branch('.')
804
        branch.repository.lock_write()
805
        self.addCleanup(branch.repository.unlock)
806
        builder = branch.repository.get_commit_builder(branch, [],
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
807
            branch.get_config_stack())
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
808
        self.addCleanup(branch.repository.abort_write_group)
809
        self.assertRaises(ValueError, builder.commit,
810
            u'Invalid\r\ncommit message\r\n')
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
811
5485.4.2 by Martin
Move guard to CommitBuilder.__init__ and test to bt.per_repository
812
    def test_non_ascii_str_committer_rejected(self):
813
        """Ensure an error is raised on a non-ascii byte string committer"""
814
        branch = self.make_branch('.')
815
        branch.repository.lock_write()
816
        self.addCleanup(branch.repository.unlock)
817
        self.assertRaises(UnicodeDecodeError,
818
            branch.repository.get_commit_builder,
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
819
            branch, [], branch.get_config_stack(),
5485.4.2 by Martin
Move guard to CommitBuilder.__init__ and test to bt.per_repository
820
            committer="Erik B\xe5gfors <erik@example.com>")
821
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
822
    def test_stacked_repositories_reject_commit_builder(self):
823
        # As per bug 375013, committing to stacked repositories is currently
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
824
        # broken if we aren't in a chk repository. So old repositories with
825
        # fallbacks refuse to hand out a commit builder.
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
826
        repo_basis = self.make_repository('basis')
827
        branch = self.make_branch('local')
828
        repo_local = branch.repository
829
        try:
830
            repo_local.add_fallback_repository(repo_basis)
831
        except errors.UnstackableRepositoryFormat:
832
            raise tests.TestNotApplicable("not a stackable format.")
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
833
        self.addCleanup(repo_local.lock_write().unlock)
834
        if not repo_local._format.supports_chks:
835
            self.assertRaises(errors.BzrError, repo_local.get_commit_builder,
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
836
                branch, [], branch.get_config_stack())
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
837
        else:
838
            builder = repo_local.get_commit_builder(branch, [],
6351.3.3 by Jelmer Vernooij
Convert more stuff to use config stacks.
839
                                                    branch.get_config_stack())
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
840
            builder.abort()
5050.18.1 by Aaron Bentley
CommitBuilder user committer, not username in revision-id.
841
842
    def test_committer_no_username(self):
843
        # Ensure that when no username is available but a committer is
844
        # supplied, commit works.
5570.3.9 by Vincent Ladeuil
More use cases for overrideEnv, _cleanEnvironment *may* contain too much variables now.
845
        self.overrideEnv('EMAIL', None)
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
846
        self.overrideEnv('BRZ_EMAIL', None)
5609.31.2 by mbp at sourcefrog
Also turn off whoami inference in per_repository tests
847
        # Also, make sure that it's not inferred from mailname.
848
        self.overrideAttr(config, '_auto_user_id',
849
            lambda: (None, None))
5050.18.1 by Aaron Bentley
CommitBuilder user committer, not username in revision-id.
850
        tree = self.make_branch_and_tree(".")
851
        tree.lock_write()
852
        try:
853
            # Make sure no username is available.
6737 by Jelmer Vernooij
Merge lp:~jelmer/brz/move-errors-config.
854
            self.assertRaises(config.NoWhoami, tree.branch.get_commit_builder,
5050.18.1 by Aaron Bentley
CommitBuilder user committer, not username in revision-id.
855
                              [])
856
            builder = tree.branch.get_commit_builder(
857
                [], committer='me@example.com')
858
            try:
859
                list(builder.record_iter_changes(tree, tree.last_revision(),
860
                    tree.iter_changes(tree.basis_tree())))
861
                builder.finish_inventory()
862
            except:
863
                builder.abort()
864
                raise
865
            repo = tree.branch.repository
866
            repo.commit_write_group()
867
        finally:
868
            tree.unlock()