~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1908.5.2 by Robert Collins
Create and test set_parent_trees.
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
1908.5.2 by Robert Collins
Create and test set_parent_trees.
16
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
17
"""Tests of the parent related functions of WorkingTrees."""
18
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
19
from cStringIO import StringIO
1908.5.2 by Robert Collins
Create and test set_parent_trees.
20
import os
21
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
22
from bzrlib import (
23
    errors,
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
24
    osutils,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
25
    revision as _mod_revision,
4095.3.4 by Vincent Ladeuil
Some trivial cleanups, related to fix for bug #272444.
26
    tests,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
27
    )
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
28
from bzrlib.inventory import (
29
    Inventory,
30
    InventoryFile,
31
    InventoryDirectory,
32
    InventoryLink,
33
    )
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
34
from bzrlib.revisiontree import InventoryRevisionTree
4523.1.4 by Martin Pool
Rename remaining *_implementations tests
35
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
36
from bzrlib.tests import (
37
    features,
38
    )
1908.5.2 by Robert Collins
Create and test set_parent_trees.
39
from bzrlib.uncommit import uncommit
40
41
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
42
class TestParents(TestCaseWithWorkingTree):
43
44
    def assertConsistentParents(self, expected, tree):
1908.7.6 by Robert Collins
Deprecate WorkingTree.last_revision.
45
        """Check that the parents found are as expected.
46
47
        This test helper also checks that they are consistent with
48
        the pre-get_parent_ids() api - which is now deprecated.
49
        """
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
50
        self.assertEqual(expected, tree.get_parent_ids())
51
        if expected == []:
2598.5.7 by Aaron Bentley
Updates from review
52
            self.assertEqual(_mod_revision.NULL_REVISION,
53
                             _mod_revision.ensure_null(tree.last_revision()))
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
54
        else:
1908.7.11 by Robert Collins
Merge bzr.dev and undeprecated WorkingTree.last_revision as per review feedback.
55
            self.assertEqual(expected[0], tree.last_revision())
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
56
57
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
58
class TestGetParents(TestParents):
59
60
    def test_get_parents(self):
61
        t = self.make_branch_and_tree('.')
62
        self.assertEqual([], t.get_parent_ids())
63
64
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
65
class TestSetParents(TestParents):
1908.5.2 by Robert Collins
Create and test set_parent_trees.
66
67
    def test_set_no_parents(self):
68
        t = self.make_branch_and_tree('.')
69
        t.set_parent_trees([])
70
        self.assertEqual([], t.get_parent_ids())
71
        # now give it a real parent, and then set it to no parents again.
72
        t.commit('first post')
73
        t.set_parent_trees([])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
74
        self.assertConsistentParents([], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
75
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
76
    def test_set_null_parent(self):
77
        t = self.make_branch_and_tree('.')
78
        self.assertRaises(errors.ReservedId, t.set_parent_ids, ['null:'],
79
                          allow_leftmost_as_ghost=True)
80
        self.assertRaises(errors.ReservedId, t.set_parent_trees,
81
                          [('null:', None)], allow_leftmost_as_ghost=True)
82
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
83
    def test_set_one_ghost_parent_rejects(self):
84
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
85
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
86
            t.set_parent_trees, [('missing-revision-id', None)])
87
88
    def test_set_one_ghost_parent_force(self):
89
        t = self.make_branch_and_tree('.')
90
        t.set_parent_trees([('missing-revision-id', None)],
91
            allow_leftmost_as_ghost=True)
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
92
        self.assertConsistentParents(['missing-revision-id'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
93
94
    def test_set_two_parents_one_ghost(self):
95
        t = self.make_branch_and_tree('.')
96
        revision_in_repo = t.commit('first post')
97
        # remove the tree's history
98
        uncommit(t.branch, tree=t)
99
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
100
        t.set_parent_trees([(revision_in_repo, rev_tree),
101
            ('another-missing', None)])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
102
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
103
104
    def test_set_three_parents(self):
105
        t = self.make_branch_and_tree('.')
106
        first_revision = t.commit('first post')
107
        uncommit(t.branch, tree=t)
108
        second_revision = t.commit('second post')
109
        uncommit(t.branch, tree=t)
110
        third_revision = t.commit('third post')
111
        uncommit(t.branch, tree=t)
112
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
113
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
114
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
115
        t.set_parent_trees([(first_revision, rev_tree1),
116
            (second_revision, rev_tree2),
117
            (third_revision, rev_tree3)])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
118
        self.assertConsistentParents(
119
            [first_revision, second_revision, third_revision], t)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
120
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
121
    def test_set_no_parents_ids(self):
122
        t = self.make_branch_and_tree('.')
123
        t.set_parent_ids([])
124
        self.assertEqual([], t.get_parent_ids())
125
        # now give it a real parent, and then set it to no parents again.
126
        t.commit('first post')
127
        t.set_parent_ids([])
128
        self.assertConsistentParents([], t)
129
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
130
    def test_set_one_ghost_parent_ids_rejects(self):
131
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
132
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
133
            t.set_parent_ids, ['missing-revision-id'])
134
135
    def test_set_one_ghost_parent_ids_force(self):
136
        t = self.make_branch_and_tree('.')
137
        t.set_parent_ids(['missing-revision-id'],
138
            allow_leftmost_as_ghost=True)
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
139
        self.assertConsistentParents(['missing-revision-id'], t)
140
141
    def test_set_two_parents_one_ghost_ids(self):
142
        t = self.make_branch_and_tree('.')
143
        revision_in_repo = t.commit('first post')
144
        # remove the tree's history
145
        uncommit(t.branch, tree=t)
146
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
147
        t.set_parent_ids([revision_in_repo, 'another-missing'])
148
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
149
150
    def test_set_three_parents_ids(self):
151
        t = self.make_branch_and_tree('.')
152
        first_revision = t.commit('first post')
153
        uncommit(t.branch, tree=t)
154
        second_revision = t.commit('second post')
155
        uncommit(t.branch, tree=t)
156
        third_revision = t.commit('third post')
157
        uncommit(t.branch, tree=t)
158
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
159
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
160
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
161
        t.set_parent_ids([first_revision, second_revision, third_revision])
162
        self.assertConsistentParents(
163
            [first_revision, second_revision, third_revision], t)
164
3462.1.2 by John Arbash Meinel
Change WT.set_parent_(ids/trees) to filter out ancestors.
165
    def test_set_duplicate_parent_ids(self):
166
        t = self.make_branch_and_tree('.')
167
        rev1 = t.commit('first post')
168
        uncommit(t.branch, tree=t)
169
        rev2 = t.commit('second post')
170
        uncommit(t.branch, tree=t)
171
        rev3 = t.commit('third post')
172
        uncommit(t.branch, tree=t)
173
        t.set_parent_ids([rev1, rev2, rev2, rev3])
174
        # We strip the duplicate, but preserve the ordering
175
        self.assertConsistentParents([rev1, rev2, rev3], t)
176
177
    def test_set_duplicate_parent_trees(self):
178
        t = self.make_branch_and_tree('.')
179
        rev1 = t.commit('first post')
180
        uncommit(t.branch, tree=t)
181
        rev2 = t.commit('second post')
182
        uncommit(t.branch, tree=t)
183
        rev3 = t.commit('third post')
184
        uncommit(t.branch, tree=t)
185
        rev_tree1 = t.branch.repository.revision_tree(rev1)
186
        rev_tree2 = t.branch.repository.revision_tree(rev2)
187
        rev_tree3 = t.branch.repository.revision_tree(rev3)
188
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
189
                            (rev2, rev_tree2), (rev3, rev_tree3)])
190
        # We strip the duplicate, but preserve the ordering
191
        self.assertConsistentParents([rev1, rev2, rev3], t)
192
193
    def test_set_parent_ids_in_ancestry(self):
194
        t = self.make_branch_and_tree('.')
195
        rev1 = t.commit('first post')
196
        rev2 = t.commit('second post')
197
        rev3 = t.commit('third post')
198
        # Reset the tree, back to rev1
199
        t.set_parent_ids([rev1])
200
        t.branch.set_last_revision_info(1, rev1)
201
        self.assertConsistentParents([rev1], t)
202
        t.set_parent_ids([rev1, rev2, rev3])
203
        # rev2 is in the ancestry of rev3, so it will be filtered out
204
        self.assertConsistentParents([rev1, rev3], t)
205
        # Order should be preserved, and the first revision should always be
206
        # kept
207
        t.set_parent_ids([rev2, rev3, rev1])
208
        self.assertConsistentParents([rev2, rev3], t)
209
210
    def test_set_parent_trees_in_ancestry(self):
211
        t = self.make_branch_and_tree('.')
212
        rev1 = t.commit('first post')
213
        rev2 = t.commit('second post')
214
        rev3 = t.commit('third post')
215
        # Reset the tree, back to rev1
216
        t.set_parent_ids([rev1])
217
        t.branch.set_last_revision_info(1, rev1)
218
        self.assertConsistentParents([rev1], t)
219
        rev_tree1 = t.branch.repository.revision_tree(rev1)
220
        rev_tree2 = t.branch.repository.revision_tree(rev2)
221
        rev_tree3 = t.branch.repository.revision_tree(rev3)
222
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
223
                            (rev3, rev_tree3)])
224
        # rev2 is in the ancestry of rev3, so it will be filtered out
225
        self.assertConsistentParents([rev1, rev3], t)
226
        # Order should be preserved, and the first revision should always be
227
        # kept
228
        t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
229
                            (rev3, rev_tree3)])
230
        self.assertConsistentParents([rev2, rev3], t)
231
3763.9.7 by Daniel Clemente
Tested Unicode target rather than always trying to create it in UTF-8. Test renamed
232
    def test_unicode_symlink(self):
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
233
        # this tests bug #272444
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
234
        self.requireFeature(features.SymlinkFeature)
235
        self.requireFeature(features.UnicodeFilenameFeature)
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
236
3763.9.3 by Daniel Clemente
Clearer test with better names and just one parent
237
        tree = self.make_branch_and_tree('tree1')
3763.9.2 by Daniel Clemente
Adapted test to use set_parent_ids and raise KnownFailure
238
3763.9.9 by Daniel Clemente
Used a greek omega instead of an accented 'o' to avoid combining characters
239
        # The link points to a file whose name is an omega
240
        # U+03A9 GREEK CAPITAL LETTER OMEGA
241
        # UTF-8: ce a9  UTF-16BE: 03a9  Decimal: Ω
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
242
        target = u'\u03a9'
243
        link_name = u'\N{Euro Sign}link'
244
        os.symlink(target, 'tree1/' + link_name)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
245
        tree.add([link_name], ['link-id'])
3763.9.2 by Daniel Clemente
Adapted test to use set_parent_ids and raise KnownFailure
246
4095.3.1 by Vincent Ladeuil
Fix #339055 and #277444 by handling non ascii symlink targets.
247
        revision1 = tree.commit('added a link to a Unicode target')
248
        revision2 = tree.commit('this revision will be discarded')
249
        tree.set_parent_ids([revision1])
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
250
        tree.lock_read()
251
        self.addCleanup(tree.unlock)
252
        # Check that the symlink target is safely round-tripped in the trees.
253
        self.assertEqual(target, tree.get_symlink_target('link-id'))
254
        basis = tree.basis_tree()
255
        self.assertEqual(target, basis.get_symlink_target('link-id'))
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
256
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
257
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
258
class TestAddParent(TestParents):
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
259
260
    def test_add_first_parent_id(self):
261
        """Test adding the first parent id"""
262
        tree = self.make_branch_and_tree('.')
263
        first_revision = tree.commit('first post')
264
        uncommit(tree.branch, tree=tree)
265
        tree.add_parent_tree_id(first_revision)
266
        self.assertConsistentParents([first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
267
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
268
    def test_add_first_parent_id_ghost_rejects(self):
269
        """Test adding the first parent id - as a ghost"""
270
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
271
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
272
            tree.add_parent_tree_id, 'first-revision')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
273
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
274
    def test_add_first_parent_id_ghost_force(self):
275
        """Test adding the first parent id - as a ghost"""
276
        tree = self.make_branch_and_tree('.')
277
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
278
        self.assertConsistentParents(['first-revision'], tree)
1908.5.13 by Robert Collins
Adding a parent when the first is a ghost already should not require forcing it.
279
280
    def test_add_second_parent_id_with_ghost_first(self):
281
        """Test adding the second parent when the first is a ghost."""
282
        tree = self.make_branch_and_tree('.')
283
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
284
        tree.add_parent_tree_id('second')
285
        self.assertConsistentParents(['first-revision', 'second'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
286
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
287
    def test_add_second_parent_id(self):
288
        """Test adding the second parent id"""
289
        tree = self.make_branch_and_tree('.')
290
        first_revision = tree.commit('first post')
291
        uncommit(tree.branch, tree=tree)
292
        second_revision = tree.commit('second post')
293
        tree.add_parent_tree_id(first_revision)
294
        self.assertConsistentParents([second_revision, first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
295
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
296
    def test_add_second_parent_id_ghost(self):
297
        """Test adding the second parent id - as a ghost"""
298
        tree = self.make_branch_and_tree('.')
299
        first_revision = tree.commit('first post')
300
        tree.add_parent_tree_id('second')
301
        self.assertConsistentParents([first_revision, 'second'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
302
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
303
    def test_add_first_parent_tree(self):
304
        """Test adding the first parent id"""
305
        tree = self.make_branch_and_tree('.')
306
        first_revision = tree.commit('first post')
307
        uncommit(tree.branch, tree=tree)
308
        tree.add_parent_tree((first_revision,
309
            tree.branch.repository.revision_tree(first_revision)))
310
        self.assertConsistentParents([first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
311
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
312
    def test_add_first_parent_tree_ghost_rejects(self):
313
        """Test adding the first parent id - as a ghost"""
314
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
315
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
316
            tree.add_parent_tree, ('first-revision', None))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
317
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
318
    def test_add_first_parent_tree_ghost_force(self):
319
        """Test adding the first parent id - as a ghost"""
320
        tree = self.make_branch_and_tree('.')
321
        tree.add_parent_tree(('first-revision', None),
322
            allow_leftmost_as_ghost=True)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
323
        self.assertConsistentParents(['first-revision'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
324
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
325
    def test_add_second_parent_tree(self):
326
        """Test adding the second parent id"""
327
        tree = self.make_branch_and_tree('.')
328
        first_revision = tree.commit('first post')
329
        uncommit(tree.branch, tree=tree)
330
        second_revision = tree.commit('second post')
331
        tree.add_parent_tree((first_revision,
332
            tree.branch.repository.revision_tree(first_revision)))
333
        self.assertConsistentParents([second_revision, first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
334
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
335
    def test_add_second_parent_tree_ghost(self):
336
        """Test adding the second parent id - as a ghost"""
337
        tree = self.make_branch_and_tree('.')
338
        first_revision = tree.commit('first post')
339
        tree.add_parent_tree(('second', None))
340
        self.assertConsistentParents([first_revision, 'second'], tree)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
341
342
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
343
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
2903.2.7 by Martin Pool
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta
344
    """Tests for the update_basis_by_delta call.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
345
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
346
    This is intuitively defined as 'apply an inventory delta to the basis and
347
    discard other parents', but for trees that have an inventory that is not
348
    managed as a tree-by-id, the implementation requires roughly duplicated
349
    tests with those for apply_inventory_delta on the main tree.
350
    """
351
352
    def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
353
        expected_inventory):
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
354
        tree.lock_write()
355
        try:
356
            tree.update_basis_by_delta(revid, delta)
357
        finally:
358
            tree.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
359
        # check the last revision was adjusted to rev_id
360
        self.assertEqual(revid, tree.last_revision())
361
        # check the parents are what we expect
362
        self.assertEqual([revid], tree.get_parent_ids())
363
        # check that the basis tree has the inventory we expect from applying
364
        # the delta.
365
        result_basis = tree.basis_tree()
366
        result_basis.lock_read()
3619.6.6 by Mark Hammond
eagerly unlock the result_basis to prevent handles staying open.
367
        try:
6405.2.9 by Jelmer Vernooij
More test fixes.
368
            self.assertEqual(expected_inventory, result_basis.root_inventory)
3619.6.6 by Mark Hammond
eagerly unlock the result_basis to prevent handles staying open.
369
        finally:
370
            result_basis.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
371
372
    def make_inv_delta(self, old, new):
373
        """Make an inventory delta from two inventories."""
374
        old_ids = set(old._byid.iterkeys())
375
        new_ids = set(new._byid.iterkeys())
376
        adds = new_ids - old_ids
377
        deletes = old_ids - new_ids
378
        common = old_ids.intersection(new_ids)
379
        delta = []
380
        for file_id in deletes:
381
            delta.append((old.id2path(file_id), None, file_id, None))
382
        for file_id in adds:
383
            delta.append((None, new.id2path(file_id), file_id, new[file_id]))
384
        for file_id in common:
385
            if old[file_id] != new[file_id]:
386
                delta.append((old.id2path(file_id), new.id2path(file_id),
387
                    file_id, new[file_id]))
388
        return delta
389
390
    def fake_up_revision(self, tree, revid, shape):
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
391
392
        class ShapeTree(InventoryRevisionTree):
393
394
            def __init__(self, shape):
395
                self._repository = tree.branch.repository
396
                self._inventory = shape
397
398
            def get_file_text(self, file_id, path=None):
6405.2.10 by Jelmer Vernooij
Fix more tests.
399
                ie = self.root_inventory[file_id]
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
400
                if ie.kind != "file":
401
                    return ""
402
                return 'a' * ie.text_size
403
404
            def get_file(self, file_id, path=None):
405
                return StringIO(self.get_file_text(file_id))
406
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
407
        tree.lock_write()
408
        try:
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
409
            if shape.root.revision is None:
410
                shape.root.revision = revid
411
            builder = tree.branch.get_commit_builder(
412
                    parents=[],
413
                    timestamp=0,
414
                    timezone=None,
415
                    committer="Foo Bar <foo@example.com>",
416
                    revision_id=revid)
417
            shape_tree = ShapeTree(shape)
5809.4.3 by Jelmer Vernooij
Fix base revid.
418
            base_tree = tree.branch.repository.revision_tree(
419
                    _mod_revision.NULL_REVISION)
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
420
            changes = shape_tree.iter_changes(
5809.4.3 by Jelmer Vernooij
Fix base revid.
421
                base_tree)
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
422
            list(builder.record_iter_changes(shape_tree,
5809.4.3 by Jelmer Vernooij
Fix base revid.
423
                base_tree.get_revision_id(), changes))
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
424
            builder.finish_inventory()
425
            builder.commit("Message")
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
426
        finally:
427
            tree.unlock()
428
429
    def add_entry(self, inv, rev_id, entry):
430
        entry.revision = rev_id
431
        inv.add(entry)
432
433
    def add_dir(self, inv, rev_id, file_id, parent_id, name):
434
        new_dir = InventoryDirectory(file_id, name, parent_id)
435
        self.add_entry(inv, rev_id, new_dir)
436
437
    def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
438
        new_file = InventoryFile(file_id, name, parent_id)
439
        new_file.text_sha1 = sha
440
        new_file.text_size = size
441
        self.add_entry(inv, rev_id, new_file)
442
443
    def add_link(self, inv, rev_id, file_id, parent_id, name, target):
444
        new_link = InventoryLink(file_id, name, parent_id)
445
        new_link.symlink_target = target
446
        self.add_entry(inv, rev_id, new_link)
447
448
    def add_new_root(self, new_shape, old_revid, new_revid):
449
        if self.bzrdir_format.repository_format.rich_root_data:
450
            self.add_dir(new_shape, old_revid, 'root-id', None, '')
451
        else:
452
            self.add_dir(new_shape, new_revid, 'root-id', None, '')
453
454
    def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
455
        new_shape, new_revid, extra_parent=None):
2889.1.1 by Robert Collins
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
456
        # set the inventory revision ids.
457
        basis_shape.revision_id = basis_revid
458
        new_shape.revision_id = new_revid
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
459
        delta = self.make_inv_delta(basis_shape, new_shape)
460
        tree = self.make_branch_and_tree('tree')
461
        # the shapes need to be in the tree's repository to be able to set them
462
        # as a parent, but the file content is not needed.
463
        if basis_revid is not None:
464
            self.fake_up_revision(tree, basis_revid, basis_shape)
465
            parents = [basis_revid]
466
            if extra_parent is not None:
467
                parents.append(extra_parent)
468
            tree.set_parent_ids(parents)
469
        self.fake_up_revision(tree, new_revid, new_shape)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
470
        # give tree an inventory of new_shape
471
        tree._write_inventory(new_shape)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
472
        self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
473
            delta, new_shape)
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
474
        # The tree should be internally consistent; while this is a moderately
475
        # large hammer, this is a particularly sensitive area of code, so the
476
        # extra assurance is well worth it.
477
        tree._validate()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
478
        osutils.rmtree('tree')
479
480
    def test_no_parents_just_root(self):
481
        """Test doing an empty commit - no parent, set a root only."""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
482
        basis_shape = Inventory(root_id=None)  # empty tree
483
        new_shape = Inventory()  # tree with a root
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
484
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
485
            'new_parent')
486
487
    def test_no_parents_full_tree(self):
488
        """Test doing a regular initial commit with files and dirs."""
489
        basis_shape = Inventory(root_id=None) # empty tree
490
        revid = 'new-parent'
491
        new_shape = Inventory(root_id=None)
492
        self.add_dir(new_shape, revid, 'root-id', None, '')
493
        self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
494
        self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
495
            12)
496
        self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
497
        self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
498
            '2' * 32, 24)
499
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
500
            revid)
501
502
    def test_file_content_change(self):
503
        old_revid = 'old-parent'
504
        basis_shape = Inventory(root_id=None)
505
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
506
        self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
507
            '1' * 32, 12)
508
        new_revid = 'new-parent'
509
        new_shape = Inventory(root_id=None)
510
        self.add_new_root(new_shape, old_revid, new_revid)
511
        self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
512
            '2' * 32, 24)
513
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
514
            new_shape, new_revid)
515
516
    def test_link_content_change(self):
517
        old_revid = 'old-parent'
518
        basis_shape = Inventory(root_id=None)
519
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
520
        self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
521
            'old-target')
522
        new_revid = 'new-parent'
523
        new_shape = Inventory(root_id=None)
524
        self.add_new_root(new_shape, old_revid, new_revid)
525
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
526
            'new-target')
527
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
528
            new_shape, new_revid)
529
530
    def test_kind_changes(self):
531
        def do_file(inv, revid):
532
            self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
533
                12)
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
534
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
535
        def do_link(inv, revid):
536
            self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
537
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
538
        def do_dir(inv, revid):
539
            self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
540
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
541
        for old_factory in (do_file, do_link, do_dir):
542
            for new_factory in (do_file, do_link, do_dir):
543
                if old_factory == new_factory:
544
                    continue
545
                old_revid = 'old-parent'
546
                basis_shape = Inventory(root_id=None)
547
                self.add_dir(basis_shape, old_revid, 'root-id', None, '')
548
                old_factory(basis_shape, old_revid)
549
                new_revid = 'new-parent'
550
                new_shape = Inventory(root_id=None)
551
                self.add_new_root(new_shape, old_revid, new_revid)
552
                new_factory(new_shape, new_revid)
553
                self.assertTransitionFromBasisToShape(basis_shape, old_revid,
554
                    new_shape, new_revid)
555
556
    def test_content_from_second_parent_is_dropped(self):
557
        left_revid = 'left-parent'
558
        basis_shape = Inventory(root_id=None)
559
        self.add_dir(basis_shape, left_revid, 'root-id', None, '')
560
        self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
561
            'left-target')
562
        # the right shape has content - file, link, subdir with a child,
563
        # that should all be discarded by the call.
564
        right_revid = 'right-parent'
565
        right_shape = Inventory(root_id=None)
566
        self.add_dir(right_shape, left_revid, 'root-id', None, '')
567
        self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
2865.1.3 by Robert Collins
Review feedback.
568
            'some-target')
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
569
        self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
570
        self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
571
            '2' * 32, 24)
572
        new_revid = 'new-parent'
573
        new_shape = Inventory(root_id=None)
574
        self.add_new_root(new_shape, left_revid, new_revid)
575
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
576
            'new-target')
577
        self.assertTransitionFromBasisToShape(basis_shape, left_revid,
578
            new_shape, new_revid, right_revid)
579
580
    def test_parent_id_changed(self):
2865.1.3 by Robert Collins
Review feedback.
581
        # test that when the only change to an entry is its parent id changing
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
582
        # that it is handled correctly (that is it keeps the same path)
583
        old_revid = 'old-parent'
584
        basis_shape = Inventory(root_id=None)
585
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
586
        self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
587
        self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
588
        new_revid = 'new-parent'
589
        new_shape = Inventory(root_id=None)
590
        self.add_new_root(new_shape, old_revid, new_revid)
591
        self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
592
        self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
593
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
594
            new_shape, new_revid)
595
596
    def test_name_changed(self):
2865.1.3 by Robert Collins
Review feedback.
597
        # test that when the only change to an entry is its name changing that
598
        # it is handled correctly (that is it keeps the same parent id)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
599
        old_revid = 'old-parent'
600
        basis_shape = Inventory(root_id=None)
601
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
602
        self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
603
        self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
604
        new_revid = 'new-parent'
605
        new_shape = Inventory(root_id=None)
606
        self.add_new_root(new_shape, old_revid, new_revid)
607
        self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
608
        self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
609
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
610
            new_shape, new_revid)
611
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
612
    def test_parent_child_swap(self):
613
        # test a A->A/B and A/B->A path swap.
614
        old_revid = 'old-parent'
615
        basis_shape = Inventory(root_id=None)
616
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
617
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
618
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
619
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
620
        new_revid = 'new-parent'
621
        new_shape = Inventory(root_id=None)
622
        self.add_new_root(new_shape, old_revid, new_revid)
623
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
624
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
625
        self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
626
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
627
            new_shape, new_revid)
628
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
629
    def test_parent_deleted_child_renamed(self):
630
        # test a A->None and A/B->A.
631
        old_revid = 'old-parent'
632
        basis_shape = Inventory(root_id=None)
633
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
634
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
635
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
636
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
637
        new_revid = 'new-parent'
638
        new_shape = Inventory(root_id=None)
639
        self.add_new_root(new_shape, old_revid, new_revid)
640
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
641
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
642
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
643
            new_shape, new_revid)
644
645
    def test_dir_to_root(self):
646
        # test a A->''.
647
        old_revid = 'old-parent'
648
        basis_shape = Inventory(root_id=None)
649
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
650
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
651
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
652
        new_revid = 'new-parent'
653
        new_shape = Inventory(root_id=None)
654
        self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
655
        self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
656
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
657
            new_shape, new_revid)
658
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
659
    def test_path_swap(self):
660
        # test a A->B and B->A path swap.
661
        old_revid = 'old-parent'
662
        basis_shape = Inventory(root_id=None)
663
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
664
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
665
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
666
        self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
667
        self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
668
        self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
669
            '1' * 32, 12)
670
        self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
671
            '2' * 32, 24)
672
        new_revid = 'new-parent'
673
        new_shape = Inventory(root_id=None)
674
        self.add_new_root(new_shape, old_revid, new_revid)
675
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
676
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
677
        self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
678
        self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
679
        self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
680
            '1' * 32, 12)
681
        self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
682
            '2' * 32, 24)
683
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
684
            new_shape, new_revid)
685
686
    def test_adds(self):
687
        # test adding paths and dirs, including adding to a newly added dir.
688
        old_revid = 'old-parent'
689
        basis_shape = Inventory(root_id=None)
690
        # with a root, so its a commit after the first.
691
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
692
        new_revid = 'new-parent'
693
        new_shape = Inventory(root_id=None)
694
        self.add_new_root(new_shape, old_revid, new_revid)
695
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
696
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
697
        self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
698
            '1' * 32, 12)
699
        self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
700
            '2' * 32, 24)
701
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
702
            new_shape, new_revid)
703
704
    def test_removes(self):
705
        # test removing paths, including paths that are within other also
706
        # removed paths.
707
        old_revid = 'old-parent'
708
        basis_shape = Inventory(root_id=None)
709
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
710
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
711
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
712
        self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
713
            '1' * 32, 12)
714
        self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
715
            '2' * 32, 24)
716
        new_revid = 'new-parent'
717
        new_shape = Inventory(root_id=None)
718
        self.add_new_root(new_shape, old_revid, new_revid)
719
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
720
            new_shape, new_revid)
721
722
    def test_move_to_added_dir(self):
723
        old_revid = 'old-parent'
724
        basis_shape = Inventory(root_id=None)
725
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
726
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
727
        new_revid = 'new-parent'
728
        new_shape = Inventory(root_id=None)
729
        self.add_new_root(new_shape, old_revid, new_revid)
730
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
731
        self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
732
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
733
            new_shape, new_revid)
734
735
    def test_move_from_removed_dir(self):
736
        old_revid = 'old-parent'
737
        basis_shape = Inventory(root_id=None)
738
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
739
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
740
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
741
        new_revid = 'new-parent'
742
        new_shape = Inventory(root_id=None)
743
        self.add_new_root(new_shape, old_revid, new_revid)
744
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
745
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
746
            new_shape, new_revid)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
747
748
    def test_move_moves_children_recursively(self):
749
        old_revid = 'old-parent'
750
        basis_shape = Inventory(root_id=None)
751
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
752
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
753
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
754
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
755
        new_revid = 'new-parent'
756
        new_shape = Inventory(root_id=None)
757
        self.add_new_root(new_shape, old_revid, new_revid)
758
        # the moved path:
759
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
760
        # unmoved children.
761
        self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
762
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
763
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
764
            new_shape, new_revid)