1
# Copyright (C) 2006, 2007 Canonical Ltd
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.
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.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for interface conformance of 'WorkingTree.move'"""
26
from bzrlib.workingtree_4 import WorkingTreeFormat4
27
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
30
class TestMove(TestCaseWithWorkingTree):
32
def get_tree_layout(self, tree):
33
"""Get the (path, file_id) pairs for the current tree."""
36
return [(path, ie.file_id) for path, ie
37
in tree.iter_entries_by_dir()]
41
def assertTreeLayout(self, expected, tree):
42
"""Check that the tree has the correct layout."""
43
actual = self.get_tree_layout(tree)
44
self.assertEqual(expected, actual)
46
def test_move_correct_call_named(self):
47
"""tree.move has the deprecated parameter 'to_name'.
48
It has been replaced by 'to_dir' for consistency.
49
Test the new API using named parameter
51
self.build_tree(['a1', 'sub1/'])
52
tree = self.make_branch_and_tree('.')
53
tree.add(['a1', 'sub1'])
54
tree.commit('initial commit')
55
self.assertEqual([('a1', 'sub1/a1')],
56
tree.move(['a1'], to_dir='sub1', after=False))
59
def test_move_correct_call_unnamed(self):
60
"""tree.move has the deprecated parameter 'to_name'.
61
It has been replaced by 'to_dir' for consistency.
62
Test the new API using unnamed parameter
64
self.build_tree(['a1', 'sub1/'])
65
tree = self.make_branch_and_tree('.')
66
tree.add(['a1', 'sub1'])
67
tree.commit('initial commit')
68
self.assertEqual([('a1', 'sub1/a1')],
69
tree.move(['a1'], 'sub1', after=False))
72
def test_move_deprecated_wrong_call(self):
73
"""tree.move has the deprecated parameter 'to_name'.
74
It has been replaced by 'to_dir' for consistency.
75
Test the new API using wrong parameter
77
self.build_tree(['a1', 'sub1/'])
78
tree = self.make_branch_and_tree('.')
79
tree.add(['a1', 'sub1'])
80
tree.commit('initial commit')
81
self.assertRaises(TypeError, tree.move, ['a1'],
82
to_this_parameter_does_not_exist='sub1',
86
def test_move_deprecated_call(self):
87
"""tree.move has the deprecated parameter 'to_name'.
88
It has been replaced by 'to_dir' for consistency.
89
Test the new API using deprecated parameter
91
self.build_tree(['a1', 'sub1/'])
92
tree = self.make_branch_and_tree('.')
93
tree.add(['a1', 'sub1'])
94
tree.commit('initial commit')
97
self.callDeprecated(['The parameter to_name was deprecated'
98
' in version 0.13. Use to_dir instead'],
99
tree.move, ['a1'], to_name='sub1',
102
# WorkingTreeFormat4 doesn't have to maintain api compatibility
103
# since it was deprecated before the class was introduced.
104
if not isinstance(self.workingtree_format, WorkingTreeFormat4):
108
def test_move_target_not_dir(self):
109
tree = self.make_branch_and_tree('.')
110
self.build_tree(['a'])
112
tree.commit('initial', rev_id='rev-1')
114
self.assertRaises(errors.BzrMoveFailedError,
115
tree.move, ['a'], 'not-a-dir')
118
def test_move_non_existent(self):
119
tree = self.make_branch_and_tree('.')
120
self.build_tree(['a/'])
122
tree.commit('initial', rev_id='rev-1')
123
self.assertRaises(errors.BzrMoveFailedError,
124
tree.move, ['not-a-file'], 'a')
125
self.assertRaises(errors.BzrMoveFailedError,
126
tree.move, ['not-a-file'], '')
129
def test_move_target_not_versioned(self):
130
tree = self.make_branch_and_tree('.')
131
self.build_tree(['a/', 'b'])
133
tree.commit('initial', rev_id='rev-1')
134
self.assertRaises(errors.BzrMoveFailedError,
135
tree.move, ['b'], 'a')
138
def test_move_unversioned(self):
139
tree = self.make_branch_and_tree('.')
140
self.build_tree(['a/', 'b'])
142
tree.commit('initial', rev_id='rev-1')
143
self.assertRaises(errors.BzrMoveFailedError,
144
tree.move, ['b'], 'a')
147
def test_move_multi_unversioned(self):
148
tree = self.make_branch_and_tree('.')
149
self.build_tree(['a/', 'b', 'c', 'd'])
150
tree.add(['a', 'c', 'd'], ['a-id', 'c-id', 'd-id'])
151
tree.commit('initial', rev_id='rev-1')
152
root_id = tree.get_root_id()
153
self.assertRaises(errors.BzrMoveFailedError,
154
tree.move, ['c', 'b', 'd'], 'a')
155
self.assertRaises(errors.BzrMoveFailedError,
156
tree.move, ['b', 'c', 'd'], 'a')
157
self.assertRaises(errors.BzrMoveFailedError,
158
tree.move, ['d', 'c', 'b'], 'a')
159
if osutils.lexists('a/c'):
160
# If 'c' was actually moved, then 'd' should have also been moved
161
self.assertTreeLayout([('', root_id), ('a', 'a-id'),
162
('a/c', 'c-id'), ('a/d', 'd-id')], tree)
164
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
165
('d', 'd-id')], tree)
166
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
167
('d', 'd-id')], tree.basis_tree())
170
def test_move_over_deleted(self):
171
tree = self.make_branch_and_tree('.')
172
self.build_tree(['a/', 'a/b', 'b'])
173
tree.add(['a', 'a/b', 'b'], ['a-id', 'ab-id', 'b-id'])
174
tree.commit('initial', rev_id='rev-1')
176
root_id = tree.get_root_id()
177
tree.remove(['a/b'], keep_files=False)
178
self.assertEqual([('b', 'a/b')], tree.move(['b'], 'a'))
179
self.assertTreeLayout([('', root_id),
185
def test_move_subdir(self):
186
tree = self.make_branch_and_tree('.')
187
self.build_tree(['a', 'b/', 'b/c'])
188
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
189
tree.commit('initial', rev_id='rev-1')
190
root_id = tree.get_root_id()
191
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
192
('b/c', 'c-id')], tree)
193
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
194
('b/c', 'c-id')], tree.basis_tree())
195
a_contents = tree.get_file_text('a-id')
196
self.assertEqual([('a', 'b/a')],
197
tree.move(['a'], 'b'))
198
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id'),
199
('b/c', 'c-id')], tree)
200
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
201
('b/c', 'c-id')], tree.basis_tree())
202
self.failIfExists('a')
203
self.assertFileEqual(a_contents, 'b/a')
206
def test_move_parent_dir(self):
207
tree = self.make_branch_and_tree('.')
208
self.build_tree(['a', 'b/', 'b/c'])
209
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
210
tree.commit('initial', rev_id='rev-1')
211
root_id = tree.get_root_id()
212
c_contents = tree.get_file_text('c-id')
213
self.assertEqual([('b/c', 'c')],
214
tree.move(['b/c'], ''))
215
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
216
('c', 'c-id')], tree)
217
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
218
('b/c', 'c-id')], tree.basis_tree())
219
self.failIfExists('b/c')
220
self.assertFileEqual(c_contents, 'c')
223
def test_move_fail_consistent(self):
224
tree = self.make_branch_and_tree('.')
225
self.build_tree(['a', 'b/', 'b/a', 'c'])
226
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
227
tree.commit('initial', rev_id='rev-1')
228
root_id = tree.get_root_id()
229
# Target already exists
230
self.assertRaises(errors.RenameFailedFilesExist,
231
tree.move, ['c', 'a'], 'b')
232
# 'c' may or may not have been moved, but either way the tree should
233
# maintain a consistent state.
234
if osutils.lexists('c'):
235
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
236
('c', 'c-id')], tree)
238
self.failUnlessExists('b/c')
239
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
240
('b/c', 'c-id')], tree)
241
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
242
('c', 'c-id')], tree.basis_tree())
245
def test_move_onto_self(self):
246
tree = self.make_branch_and_tree('.')
247
self.build_tree(['b/', 'b/a'])
248
tree.add(['b', 'b/a'], ['b-id', 'a-id'])
249
tree.commit('initial', rev_id='rev-1')
251
self.assertRaises(errors.BzrMoveFailedError,
252
tree.move, ['b/a'], 'b')
255
def test_move_onto_self_root(self):
256
tree = self.make_branch_and_tree('.')
257
self.build_tree(['a'])
258
tree.add(['a'], ['a-id'])
259
tree.commit('initial', rev_id='rev-1')
261
self.assertRaises(errors.BzrMoveFailedError,
262
tree.move, ['a'], 'a')
265
def test_move_after(self):
266
tree = self.make_branch_and_tree('.')
267
self.build_tree(['a', 'b/'])
268
tree.add(['a', 'b'], ['a-id', 'b-id'])
269
tree.commit('initial', rev_id='rev-1')
270
root_id = tree.get_root_id()
271
os.rename('a', 'b/a')
273
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
275
# We don't need after=True as long as source is missing and target
277
self.assertEqual([('a', 'b/a')],
278
tree.move(['a'], 'b'))
279
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
281
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
285
def test_move_after_with_after(self):
286
tree = self.make_branch_and_tree('.')
287
self.build_tree(['a', 'b/'])
288
tree.add(['a', 'b'], ['a-id', 'b-id'])
289
tree.commit('initial', rev_id='rev-1')
290
root_id = tree.get_root_id()
291
os.rename('a', 'b/a')
293
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
295
# Passing after=True should work as well
296
self.assertEqual([('a', 'b/a')],
297
tree.move(['a'], 'b', after=True))
298
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
300
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
304
def test_move_after_no_target(self):
305
tree = self.make_branch_and_tree('.')
306
self.build_tree(['a', 'b/'])
307
tree.add(['a', 'b'], ['a-id', 'b-id'])
308
tree.commit('initial', rev_id='rev-1')
309
root_id = tree.get_root_id()
311
# Passing after when the file hasn't been move raises an exception
312
self.assertRaises(errors.BzrMoveFailedError,
313
tree.move, ['a'], 'b', after=True)
314
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
318
def test_move_after_source_and_dest(self):
319
tree = self.make_branch_and_tree('.')
320
self.build_tree(['a', 'b/', 'b/a'])
321
tree.add(['a', 'b'], ['a-id', 'b-id'])
322
tree.commit('initial', rev_id='rev-1')
323
root_id = tree.get_root_id()
325
# TODO: jam 20070225 I would usually use 'rb', but assertFileEqual
327
a_file = open('a', 'r')
329
a_text = a_file.read()
332
ba_file = open('b/a', 'r')
334
ba_text = ba_file.read()
338
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
340
self.assertRaises(errors.RenameFailedFilesExist,
341
tree.move, ['a'], 'b', after=False)
342
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
344
self.assertFileEqual(a_text, 'a')
345
self.assertFileEqual(ba_text, 'b/a')
346
# But you can pass after=True
347
self.assertEqual([('a', 'b/a')],
348
tree.move(['a'], 'b', after=True))
349
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
351
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
353
# But it shouldn't actually move anything
354
self.assertFileEqual(a_text, 'a')
355
self.assertFileEqual(ba_text, 'b/a')
358
def test_move_directory(self):
359
tree = self.make_branch_and_tree('.')
360
self.build_tree(['a/', 'a/b', 'a/c/', 'a/c/d', 'e/'])
361
tree.add(['a', 'a/b', 'a/c', 'a/c/d', 'e'],
362
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
363
tree.commit('initial', rev_id='rev-1')
364
root_id = tree.get_root_id()
366
self.assertEqual([('a', 'e/a')],
367
tree.move(['a'], 'e'))
368
self.assertTreeLayout([('', root_id), ('e', 'e-id'), ('e/a', 'a-id'),
369
('e/a/b', 'b-id'), ('e/a/c', 'c-id'),
370
('e/a/c/d', 'd-id')], tree)
371
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('e', 'e-id'),
372
('a/b', 'b-id'), ('a/c', 'c-id'),
373
('a/c/d', 'd-id')], tree.basis_tree())
376
def test_move_directory_into_parent(self):
377
tree = self.make_branch_and_tree('.')
378
self.build_tree(['c/', 'c/b/', 'c/b/d/'])
379
tree.add(['c', 'c/b', 'c/b/d'],
380
['c-id', 'b-id', 'd-id'])
381
tree.commit('initial', rev_id='rev-1')
382
root_id = tree.get_root_id()
384
self.assertEqual([('c/b', 'b')],
385
tree.move(['c/b'], ''))
386
self.assertTreeLayout([('', root_id),
393
def test_move_directory_with_children_in_subdir(self):
394
tree = self.make_branch_and_tree('.')
395
self.build_tree(['a/', 'a/b', 'a/c/', 'd/'])
396
tree.add(['a', 'a/b', 'a/c', 'd'],
397
['a-id', 'b-id', 'c-id', 'd-id'])
398
tree.commit('initial', rev_id='rev-1')
399
root_id = tree.get_root_id()
402
tree.rename_one('a/b', 'a/c/b')
403
self.assertTreeLayout([('', root_id),
409
self.assertEqual([('a', 'd/a')],
410
tree.move(['a'], 'd'))
411
self.assertTreeLayout([('', root_id),
419
def test_move_directory_with_deleted_children(self):
420
tree = self.make_branch_and_tree('.')
421
self.build_tree(['a/', 'a/b', 'a/c', 'a/d', 'b/'])
422
tree.add(['a', 'b', 'a/b', 'a/c', 'a/d'],
423
['a-id', 'b-id', 'ab-id', 'ac-id', 'ad-id'])
424
tree.commit('initial', rev_id='rev-1')
425
root_id = tree.get_root_id()
427
tree.remove(['a/b', 'a/d'])
429
self.assertEqual([('a', 'b/a')],
430
tree.move(['a'], 'b'))
431
self.assertTreeLayout([('', root_id),
438
def test_move_directory_with_new_children(self):
439
tree = self.make_branch_and_tree('.')
440
self.build_tree(['a/', 'a/c', 'b/'])
441
tree.add(['a', 'b', 'a/c'], ['a-id', 'b-id', 'ac-id'])
442
tree.commit('initial', rev_id='rev-1')
443
root_id = tree.get_root_id()
445
self.build_tree(['a/b', 'a/d'])
446
tree.add(['a/b', 'a/d'], ['ab-id', 'ad-id'])
448
self.assertEqual([('a', 'b/a')],
449
tree.move(['a'], 'b'))
450
self.assertTreeLayout([('', root_id),
459
def test_move_directory_with_moved_children(self):
460
tree = self.make_branch_and_tree('.')
461
self.build_tree(['a/', 'a/b', 'a/c', 'd', 'e/'])
462
tree.add(['a', 'a/b', 'a/c', 'd', 'e'],
463
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
464
tree.commit('initial', rev_id='rev-1')
465
root_id = tree.get_root_id()
467
self.assertEqual([('a/b', 'b')],
468
tree.move(['a/b'], ''))
469
self.assertTreeLayout([('', root_id),
476
self.assertEqual([('d', 'a/d')],
477
tree.move(['d'], 'a'))
478
self.assertTreeLayout([('', root_id),
485
self.assertEqual([('a', 'e/a')],
486
tree.move(['a'], 'e'))
487
self.assertTreeLayout([('', root_id),
496
def test_move_directory_with_renamed_child(self):
497
tree = self.make_branch_and_tree('.')
498
self.build_tree(['a/', 'a/b', 'a/c', 'd/'])
499
tree.add(['a', 'a/b', 'a/c', 'd'],
500
['a-id', 'b-id', 'c-id', 'd-id'])
501
tree.commit('initial', rev_id='rev-1')
502
root_id = tree.get_root_id()
504
tree.rename_one('a/b', 'a/d')
505
self.assertTreeLayout([('', root_id),
511
self.assertEqual([('a', 'd/a')],
512
tree.move(['a'], 'd'))
513
self.assertTreeLayout([('', root_id),
521
def test_move_directory_with_swapped_children(self):
522
tree = self.make_branch_and_tree('.')
523
self.build_tree(['a/', 'a/b', 'a/c', 'a/d', 'e/'])
524
tree.add(['a', 'a/b', 'a/c', 'a/d', 'e'],
525
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
526
tree.commit('initial', rev_id='rev-1')
527
root_id = tree.get_root_id()
529
tree.rename_one('a/b', 'a/bb')
530
tree.rename_one('a/d', 'a/b')
531
tree.rename_one('a/bb', 'a/d')
532
self.assertTreeLayout([('', root_id),
539
self.assertEqual([('a', 'e/a')],
540
tree.move(['a'], 'e'))
541
self.assertTreeLayout([('', root_id),
550
def test_move_moved(self):
551
"""Moving a moved entry works as expected."""
552
tree = self.make_branch_and_tree('.')
553
self.build_tree(['a/', 'a/b', 'c/'])
554
tree.add(['a', 'a/b', 'c'], ['a-id', 'b-id', 'c-id'])
555
tree.commit('initial', rev_id='rev-1')
556
root_id = tree.get_root_id()
558
self.assertEqual([('a/b', 'c/b')],
559
tree.move(['a/b'], 'c'))
560
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
561
('c/b', 'b-id')], tree)
562
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
563
('a/b', 'b-id')], tree.basis_tree())
565
self.assertEqual([('c/b', 'b')],
566
tree.move(['c/b'], ''))
567
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
568
('c', 'c-id')], tree)
569
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
570
('a/b', 'b-id')], tree.basis_tree())