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_subdir(self):
171
tree = self.make_branch_and_tree('.')
172
self.build_tree(['a', 'b/', 'b/c'])
173
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
174
tree.commit('initial', rev_id='rev-1')
175
root_id = tree.get_root_id()
176
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
177
('b/c', 'c-id')], tree)
178
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
179
('b/c', 'c-id')], tree.basis_tree())
180
a_contents = tree.get_file_text('a-id')
181
self.assertEqual([('a', 'b/a')],
182
tree.move(['a'], 'b'))
183
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id'),
184
('b/c', 'c-id')], tree)
185
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
186
('b/c', 'c-id')], tree.basis_tree())
187
self.failIfExists('a')
188
self.assertFileEqual(a_contents, 'b/a')
191
def test_move_parent_dir(self):
192
tree = self.make_branch_and_tree('.')
193
self.build_tree(['a', 'b/', 'b/c'])
194
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
195
tree.commit('initial', rev_id='rev-1')
196
root_id = tree.get_root_id()
197
c_contents = tree.get_file_text('c-id')
198
self.assertEqual([('b/c', 'c')],
199
tree.move(['b/c'], ''))
200
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
201
('c', 'c-id')], tree)
202
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
203
('b/c', 'c-id')], tree.basis_tree())
204
self.failIfExists('b/c')
205
self.assertFileEqual(c_contents, 'c')
208
def test_move_fail_consistent(self):
209
tree = self.make_branch_and_tree('.')
210
self.build_tree(['a', 'b/', 'b/a', 'c'])
211
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
212
tree.commit('initial', rev_id='rev-1')
213
root_id = tree.get_root_id()
214
# Target already exists
215
self.assertRaises(errors.RenameFailedFilesExist,
216
tree.move, ['c', 'a'], 'b')
217
# 'c' may or may not have been moved, but either way the tree should
218
# maintain a consistent state.
219
if osutils.lexists('c'):
220
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
221
('c', 'c-id')], tree)
223
self.failUnlessExists('b/c')
224
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
225
('b/c', 'c-id')], tree)
226
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
227
('c', 'c-id')], tree.basis_tree())
230
def test_move_onto_self(self):
231
tree = self.make_branch_and_tree('.')
232
self.build_tree(['b/', 'b/a'])
233
tree.add(['b', 'b/a'], ['b-id', 'a-id'])
234
tree.commit('initial', rev_id='rev-1')
236
self.assertRaises(errors.BzrMoveFailedError,
237
tree.move, ['b/a'], 'b')
240
def test_move_onto_self_root(self):
241
tree = self.make_branch_and_tree('.')
242
self.build_tree(['a'])
243
tree.add(['a'], ['a-id'])
244
tree.commit('initial', rev_id='rev-1')
246
self.assertRaises(errors.BzrMoveFailedError,
247
tree.move, ['a'], 'a')
250
def test_move_after(self):
251
tree = self.make_branch_and_tree('.')
252
self.build_tree(['a', 'b/'])
253
tree.add(['a', 'b'], ['a-id', 'b-id'])
254
tree.commit('initial', rev_id='rev-1')
255
root_id = tree.get_root_id()
256
os.rename('a', 'b/a')
258
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
260
# We don't need after=True as long as source is missing and target
262
self.assertEqual([('a', 'b/a')],
263
tree.move(['a'], 'b'))
264
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
266
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
270
def test_move_after_with_after(self):
271
tree = self.make_branch_and_tree('.')
272
self.build_tree(['a', 'b/'])
273
tree.add(['a', 'b'], ['a-id', 'b-id'])
274
tree.commit('initial', rev_id='rev-1')
275
root_id = tree.get_root_id()
276
os.rename('a', 'b/a')
278
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
280
# Passing after=True should work as well
281
self.assertEqual([('a', 'b/a')],
282
tree.move(['a'], 'b', after=True))
283
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
285
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
289
def test_move_after_no_target(self):
290
tree = self.make_branch_and_tree('.')
291
self.build_tree(['a', 'b/'])
292
tree.add(['a', 'b'], ['a-id', 'b-id'])
293
tree.commit('initial', rev_id='rev-1')
294
root_id = tree.get_root_id()
296
# Passing after when the file hasn't been move raises an exception
297
self.assertRaises(errors.BzrMoveFailedError,
298
tree.move, ['a'], 'b', after=True)
299
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
303
def test_move_after_source_and_dest(self):
304
tree = self.make_branch_and_tree('.')
305
self.build_tree(['a', 'b/', 'b/a'])
306
tree.add(['a', 'b'], ['a-id', 'b-id'])
307
tree.commit('initial', rev_id='rev-1')
308
root_id = tree.get_root_id()
310
# TODO: jam 20070225 I would usually use 'rb', but assertFileEqual
312
a_file = open('a', 'r')
314
a_text = a_file.read()
317
ba_file = open('b/a', 'r')
319
ba_text = ba_file.read()
323
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
325
self.assertRaises(errors.RenameFailedFilesExist,
326
tree.move, ['a'], 'b', after=False)
327
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
329
self.assertFileEqual(a_text, 'a')
330
self.assertFileEqual(ba_text, 'b/a')
331
# But you can pass after=True
332
self.assertEqual([('a', 'b/a')],
333
tree.move(['a'], 'b', after=True))
334
self.assertTreeLayout([('', root_id), ('b', 'b-id'), ('b/a', 'a-id')],
336
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id')],
338
# But it shouldn't actually move anything
339
self.assertFileEqual(a_text, 'a')
340
self.assertFileEqual(ba_text, 'b/a')
343
def test_move_directory(self):
344
tree = self.make_branch_and_tree('.')
345
self.build_tree(['a/', 'a/b', 'a/c/', 'a/c/d', 'e/'])
346
tree.add(['a', 'a/b', 'a/c', 'a/c/d', 'e'],
347
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
348
tree.commit('initial', rev_id='rev-1')
349
root_id = tree.get_root_id()
351
self.assertEqual([('a', 'e/a')],
352
tree.move(['a'], 'e'))
353
self.assertTreeLayout([('', root_id), ('e', 'e-id'), ('e/a', 'a-id'),
354
('e/a/b', 'b-id'), ('e/a/c', 'c-id'),
355
('e/a/c/d', 'd-id')], tree)
356
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('e', 'e-id'),
357
('a/b', 'b-id'), ('a/c', 'c-id'),
358
('a/c/d', 'd-id')], tree.basis_tree())
361
def test_move_directory_into_parent(self):
362
tree = self.make_branch_and_tree('.')
363
self.build_tree(['c/', 'c/b/', 'c/b/d/'])
364
tree.add(['c', 'c/b', 'c/b/d'],
365
['c-id', 'b-id', 'd-id'])
366
tree.commit('initial', rev_id='rev-1')
367
root_id = tree.get_root_id()
369
self.assertEqual([('c/b', 'b')],
370
tree.move(['c/b'], ''))
371
self.assertTreeLayout([('', root_id),
378
def test_move_directory_with_children_in_subdir(self):
379
tree = self.make_branch_and_tree('.')
380
self.build_tree(['a/', 'a/b', 'a/c/', 'd/'])
381
tree.add(['a', 'a/b', 'a/c', 'd'],
382
['a-id', 'b-id', 'c-id', 'd-id'])
383
tree.commit('initial', rev_id='rev-1')
384
root_id = tree.get_root_id()
387
tree.rename_one('a/b', 'a/c/b')
388
self.assertTreeLayout([('', root_id),
394
self.assertEqual([('a', 'd/a')],
395
tree.move(['a'], 'd'))
396
self.assertTreeLayout([('', root_id),
404
def test_move_directory_with_deleted_children(self):
405
tree = self.make_branch_and_tree('.')
406
self.build_tree(['a/', 'a/b', 'a/c', 'a/d', 'b/'])
407
tree.add(['a', 'b', 'a/b', 'a/c', 'a/d'],
408
['a-id', 'b-id', 'ab-id', 'ac-id', 'ad-id'])
409
tree.commit('initial', rev_id='rev-1')
410
root_id = tree.get_root_id()
412
tree.remove(['a/b', 'a/d'])
414
self.assertEqual([('a', 'b/a')],
415
tree.move(['a'], 'b'))
416
self.assertTreeLayout([('', root_id),
423
def test_move_directory_with_new_children(self):
424
tree = self.make_branch_and_tree('.')
425
self.build_tree(['a/', 'a/c', 'b/'])
426
tree.add(['a', 'b', 'a/c'], ['a-id', 'b-id', 'ac-id'])
427
tree.commit('initial', rev_id='rev-1')
428
root_id = tree.get_root_id()
430
self.build_tree(['a/b', 'a/d'])
431
tree.add(['a/b', 'a/d'], ['ab-id', 'ad-id'])
433
self.assertEqual([('a', 'b/a')],
434
tree.move(['a'], 'b'))
435
self.assertTreeLayout([('', root_id),
444
def test_move_directory_with_moved_children(self):
445
tree = self.make_branch_and_tree('.')
446
self.build_tree(['a/', 'a/b', 'a/c', 'd', 'e/'])
447
tree.add(['a', 'a/b', 'a/c', 'd', 'e'],
448
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
449
tree.commit('initial', rev_id='rev-1')
450
root_id = tree.get_root_id()
452
self.assertEqual([('a/b', 'b')],
453
tree.move(['a/b'], ''))
454
self.assertTreeLayout([('', root_id),
461
self.assertEqual([('d', 'a/d')],
462
tree.move(['d'], 'a'))
463
self.assertTreeLayout([('', root_id),
470
self.assertEqual([('a', 'e/a')],
471
tree.move(['a'], 'e'))
472
self.assertTreeLayout([('', root_id),
481
def test_move_directory_with_renamed_child(self):
482
tree = self.make_branch_and_tree('.')
483
self.build_tree(['a/', 'a/b', 'a/c', 'd/'])
484
tree.add(['a', 'a/b', 'a/c', 'd'],
485
['a-id', 'b-id', 'c-id', 'd-id'])
486
tree.commit('initial', rev_id='rev-1')
487
root_id = tree.get_root_id()
489
tree.rename_one('a/b', 'a/d')
490
self.assertTreeLayout([('', root_id),
496
self.assertEqual([('a', 'd/a')],
497
tree.move(['a'], 'd'))
498
self.assertTreeLayout([('', root_id),
506
def test_move_directory_with_swapped_children(self):
507
tree = self.make_branch_and_tree('.')
508
self.build_tree(['a/', 'a/b', 'a/c', 'a/d', 'e/'])
509
tree.add(['a', 'a/b', 'a/c', 'a/d', 'e'],
510
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
511
tree.commit('initial', rev_id='rev-1')
512
root_id = tree.get_root_id()
514
tree.rename_one('a/b', 'a/bb')
515
tree.rename_one('a/d', 'a/b')
516
tree.rename_one('a/bb', 'a/d')
517
self.assertTreeLayout([('', root_id),
524
self.assertEqual([('a', 'e/a')],
525
tree.move(['a'], 'e'))
526
self.assertTreeLayout([('', root_id),
535
def test_move_moved(self):
536
"""Moving a moved entry works as expected."""
537
tree = self.make_branch_and_tree('.')
538
self.build_tree(['a/', 'a/b', 'c/'])
539
tree.add(['a', 'a/b', 'c'], ['a-id', 'b-id', 'c-id'])
540
tree.commit('initial', rev_id='rev-1')
541
root_id = tree.get_root_id()
543
self.assertEqual([('a/b', 'c/b')],
544
tree.move(['a/b'], 'c'))
545
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
546
('c/b', 'b-id')], tree)
547
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
548
('a/b', 'b-id')], tree.basis_tree())
550
self.assertEqual([('c/b', 'b')],
551
tree.move(['c/b'], ''))
552
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('b', 'b-id'),
553
('c', 'c-id')], tree)
554
self.assertTreeLayout([('', root_id), ('a', 'a-id'), ('c', 'c-id'),
555
('a/b', 'b-id')], tree.basis_tree())