187
192
self.run_script(self.preamble)
190
class TestResolveTextConflicts(TestResolveConflicts):
195
class TestResolveContentConflicts(TestResolveConflicts):
197
# FIXME: We need to add the reverse case (delete in trunk, modify in
198
# branch) but that could wait until the resolution mechanism is implemented.
203
$ echo 'trunk content' >file
205
$ bzr commit -m 'Create trunk'
207
$ bzr branch . ../branch
210
$ bzr commit -m 'Delete file'
213
$ echo 'more content' >>file
214
$ bzr commit -m 'Modify file'
219
2>Contents conflict in file
220
2>1 conflicts encountered.
223
def test_take_this(self):
225
$ bzr rm file.OTHER --force # a simple rm file.OTHER is valid too
227
$ bzr commit --strict -m 'No more conflicts nor unknown files'
230
def test_take_other(self):
232
$ bzr mv file.OTHER file
234
$ bzr commit --strict -m 'No more conflicts nor unknown files'
237
def test_resolve_taking_this(self):
239
$ bzr resolve --take-this file
240
$ bzr commit --strict -m 'No more conflicts nor unknown files'
243
def test_resolve_taking_other(self):
245
$ bzr resolve --take-other file
246
$ bzr commit --strict -m 'No more conflicts nor unknown files'
250
class TestResolveDuplicateEntry(TestResolveConflicts):
255
$ echo 'trunk content' >file
257
$ bzr commit -m 'Create trunk'
258
$ echo 'trunk content too' >file2
260
$ bzr commit -m 'Add file2 in trunk'
262
$ bzr branch . -r 1 ../branch
264
$ echo 'branch content' >file2
266
$ bzr commit -m 'Add file2 in branch'
270
2>R file2 => file2.moved
271
2>Conflict adding file file2. Moved existing file to file2.moved.
272
2>1 conflicts encountered.
275
def test_keep_this(self):
277
$ bzr rm file2 --force
278
$ bzr mv file2.moved file2
280
$ bzr commit --strict -m 'No more conflicts nor unknown files'
283
def test_keep_other(self):
284
self.failIfExists('branch/file2.moved')
286
$ bzr rm file2.moved --force
288
$ bzr commit --strict -m 'No more conflicts nor unknown files'
290
self.failIfExists('branch/file2.moved')
292
def test_resolve_taking_this(self):
294
$ bzr resolve --take-this file2
295
$ bzr commit --strict -m 'No more conflicts nor unknown files'
298
def test_resolve_taking_other(self):
300
$ bzr resolve --take-other file2
301
$ bzr commit --strict -m 'No more conflicts nor unknown files'
195
def mirror_scenarios(base_scenarios):
196
"""Return a list of mirrored scenarios.
198
Each scenario in base_scenarios is duplicated switching the roles of 'this'
202
for common, (lname, ldict), (rname, rdict) in base_scenarios:
203
a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
204
[(rname, dict(_other=rdict))])
205
b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
206
[(lname, dict(_other=ldict))])
207
# Inject the common parameters in all scenarios
208
for name, d in a + b:
210
scenarios.extend(a + b)
214
# FIXME: Get rid of parametrized (in the class name) once we delete
215
# TestResolveConflicts -- vila 20100308
216
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
217
"""This class provides a base to test single conflict resolution.
219
Since all conflict objects are created with specific semantics for their
220
attributes, each class should implement the necessary functions and
221
attributes described below.
223
Each class should define the scenarios that create the expected (single)
226
Each scenario describes:
227
* how to create 'base' tree (and revision)
228
* how to create 'left' tree (and revision, parent rev 'base')
229
* how to create 'right' tree (and revision, parent rev 'base')
230
* how to check that changes in 'base'->'left' have been taken
231
* how to check that changes in 'base'->'right' have been taken
233
From each base scenario, we generate two concrete scenarios where:
234
* this=left, other=right
235
* this=right, other=left
237
Then the test case verifies each concrete scenario by:
238
* creating a branch containing the 'base', 'this' and 'other' revisions
239
* creating a working tree for the 'this' revision
240
* performing the merge of 'other' into 'this'
241
* verifying the expected conflict was generated
242
* resolving with --take-this or --take-other, and running the corresponding
243
checks (for either 'base'->'this', or 'base'->'other')
245
:cvar _conflict_type: The expected class of the generated conflict.
247
:cvar _assert_conflict: A method receiving the working tree and the
248
conflict object and checking its attributes.
250
:cvar _base_actions: The branchbuilder actions to create the 'base'
253
:cvar _this: The dict related to 'base' -> 'this'. It contains at least:
254
* 'actions': The branchbuilder actions to create the 'this'
256
* 'check': how to check the changes after resolution with --take-this.
258
:cvar _other: The dict related to 'base' -> 'other'. It contains at least:
259
* 'actions': The branchbuilder actions to create the 'other'
261
* 'check': how to check the changes after resolution with --take-other.
264
# Set by daughter classes
265
_conflict_type = None
266
_assert_conflict = None
274
"""The scenario list for the conflict type defined by the class.
276
Each scenario is of the form:
277
(common, (left_name, left_dict), (right_name, right_dict))
281
* left_name and right_name are the scenario names that will be combined
283
* left_dict and right_dict are the attributes specific to each half of
284
the scenario. They should include at least 'actions' and 'check' and
285
will be available as '_this' and '_other' test instance attributes.
287
Daughters classes are free to add their specific attributes as they see
288
fit in any of the three dicts.
290
This is a class method so that load_tests can find it.
292
'_base_actions' in the common dict, 'actions' and 'check' in the left
293
and right dicts use names that map to methods in the test classes. Some
294
prefixes are added to these names to get the correspong methods (see
295
_get_actions() and _get_check()). The motivation here is to avoid
296
collisions in the class namespace.
300
super(TestParametrizedResolveConflicts, self).setUp()
301
builder = self.make_branch_builder('trunk')
302
builder.start_series()
304
# Create an empty trunk
305
builder.build_snapshot('start', None, [
306
('add', ('', 'root-id', 'directory', ''))])
307
# Add a minimal base content
308
base_actions = self._get_actions(self._base_actions)()
309
builder.build_snapshot('base', ['start'], base_actions)
310
# Modify the base content in branch
311
actions_other = self._get_actions(self._other['actions'])()
312
builder.build_snapshot('other', ['base'], actions_other)
313
# Modify the base content in trunk
314
actions_this = self._get_actions(self._this['actions'])()
315
builder.build_snapshot('this', ['base'], actions_this)
316
# builder.get_branch() tip is now 'this'
318
builder.finish_series()
319
self.builder = builder
321
def _get_actions(self, name):
322
return getattr(self, 'do_%s' % name)
324
def _get_check(self, name):
325
return getattr(self, 'check_%s' % name)
327
def _merge_other_into_this(self):
328
b = self.builder.get_branch()
329
wt = b.bzrdir.sprout('branch').open_workingtree()
330
wt.merge_from_branch(b, 'other')
333
def assertConflict(self, wt):
334
confs = wt.conflicts()
335
self.assertLength(1, confs)
337
self.assertIsInstance(c, self._conflict_type)
338
self._assert_conflict(wt, c)
340
def _get_resolve_path_arg(self, wt, action):
341
raise NotImplementedError(self._get_resolve_path_arg)
343
def check_resolved(self, wt, action):
344
path = self._get_resolve_path_arg(wt, action)
345
conflicts.resolve(wt, [path], action=action)
346
# Check that we don't have any conflicts nor unknown left
347
self.assertLength(0, wt.conflicts())
348
self.assertLength(0, list(wt.unknowns()))
350
def test_resolve_taking_this(self):
351
wt = self._merge_other_into_this()
352
self.assertConflict(wt)
353
self.check_resolved(wt, 'take_this')
354
check_this = self._get_check(self._this['check'])
357
def test_resolve_taking_other(self):
358
wt = self._merge_other_into_this()
359
self.assertConflict(wt)
360
self.check_resolved(wt, 'take_other')
361
check_other = self._get_check(self._other['check'])
365
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
367
_conflict_type = conflicts.TextConflict
369
# Set by the scenarios
370
# path and file-id for the file involved in the conflict
374
scenarios = mirror_scenarios(
376
# File modified on both sides
377
(dict(_base_actions='create_file',
378
_path='file', _file_id='file-id'),
380
dict(actions='modify_file_A', check='file_has_content_A')),
382
dict(actions='modify_file_B', check='file_has_content_B')),),
383
# File modified on both sides in dir
384
(dict(_base_actions='create_file_in_dir',
385
_path='dir/file', _file_id='file-id'),
386
('filed_modified_A_in_dir',
387
dict(actions='modify_file_A',
388
check='file_in_dir_has_content_A')),
390
dict(actions='modify_file_B',
391
check='file_in_dir_has_content_B')),),
394
def do_create_file(self, path='file'):
395
return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
397
def do_modify_file_A(self):
398
return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
400
def do_modify_file_B(self):
401
return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
403
def check_file_has_content_A(self, path='file'):
404
self.assertFileEqual('trunk content\nfeature A\n',
405
osutils.pathjoin('branch', path))
407
def check_file_has_content_B(self, path='file'):
408
self.assertFileEqual('trunk content\nfeature B\n',
409
osutils.pathjoin('branch', path))
411
def do_create_file_in_dir(self):
412
return [('add', ('dir', 'dir-id', 'directory', '')),
413
] + self.do_create_file('dir/file')
415
def check_file_in_dir_has_content_A(self):
416
self.check_file_has_content_A('dir/file')
418
def check_file_in_dir_has_content_B(self):
419
self.check_file_has_content_B('dir/file')
421
def _get_resolve_path_arg(self, wt, action):
424
def assertTextConflict(self, wt, c):
425
self.assertEqual(self._file_id, c.file_id)
426
self.assertEqual(self._path, c.path)
427
_assert_conflict = assertTextConflict
430
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
432
_conflict_type = conflicts.ContentsConflict
434
# Set by the scenarios
435
# path and file-id for the file involved in the conflict
439
scenarios = mirror_scenarios(
441
# File modified/deleted
442
(dict(_base_actions='create_file',
443
_path='file', _file_id='file-id'),
445
dict(actions='modify_file', check='file_has_more_content')),
447
dict(actions='delete_file', check='file_doesnt_exist')),),
448
# File renamed-modified/deleted
449
(dict(_base_actions='create_file',
450
_path='new-file', _file_id='file-id'),
451
('file_renamed_and_modified',
452
dict(actions='modify_and_rename_file',
453
check='file_renamed_and_more_content')),
455
dict(actions='delete_file', check='file_doesnt_exist')),),
456
# File modified/deleted in dir
457
(dict(_base_actions='create_file_in_dir',
458
_path='dir/file', _file_id='file-id'),
459
('file_modified_in_dir',
460
dict(actions='modify_file_in_dir',
461
check='file_in_dir_has_more_content')),
462
('file_deleted_in_dir',
463
dict(actions='delete_file',
464
check='file_in_dir_doesnt_exist')),),
467
def do_create_file(self):
468
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
470
def do_modify_file(self):
471
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
473
def do_modify_and_rename_file(self):
474
return [('modify', ('file-id', 'trunk content\nmore content\n')),
475
('rename', ('file', 'new-file'))]
477
def check_file_has_more_content(self):
478
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
480
def check_file_renamed_and_more_content(self):
481
self.assertFileEqual('trunk content\nmore content\n', 'branch/new-file')
483
def do_delete_file(self):
484
return [('unversion', 'file-id')]
486
def check_file_doesnt_exist(self):
487
self.assertPathDoesNotExist('branch/file')
489
def do_create_file_in_dir(self):
490
return [('add', ('dir', 'dir-id', 'directory', '')),
491
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
493
def do_modify_file_in_dir(self):
494
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
496
def check_file_in_dir_has_more_content(self):
497
self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
499
def check_file_in_dir_doesnt_exist(self):
500
self.assertPathDoesNotExist('branch/dir/file')
502
def _get_resolve_path_arg(self, wt, action):
505
def assertContentsConflict(self, wt, c):
506
self.assertEqual(self._file_id, c.file_id)
507
self.assertEqual(self._path, c.path)
508
_assert_conflict = assertContentsConflict
511
class TestResolvePathConflict(TestParametrizedResolveConflicts):
513
_conflict_type = conflicts.PathConflict
515
def do_nothing(self):
518
# Each side dict additionally defines:
519
# - path path involved (can be '<deleted>')
521
scenarios = mirror_scenarios(
523
# File renamed/deleted
524
(dict(_base_actions='create_file'),
526
dict(actions='rename_file', check='file_renamed',
527
path='new-file', file_id='file-id')),
529
dict(actions='delete_file', check='file_doesnt_exist',
530
# PathConflicts deletion handling requires a special
532
path='<deleted>', file_id='file-id')),),
533
# File renamed/deleted in dir
534
(dict(_base_actions='create_file_in_dir'),
535
('file_renamed_in_dir',
536
dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
537
path='dir/new-file', file_id='file-id')),
539
dict(actions='delete_file', check='file_in_dir_doesnt_exist',
540
# PathConflicts deletion handling requires a special
542
path='<deleted>', file_id='file-id')),),
543
# File renamed/renamed differently
544
(dict(_base_actions='create_file'),
546
dict(actions='rename_file', check='file_renamed',
547
path='new-file', file_id='file-id')),
549
dict(actions='rename_file2', check='file_renamed2',
550
path='new-file2', file_id='file-id')),),
551
# Dir renamed/deleted
552
(dict(_base_actions='create_dir'),
554
dict(actions='rename_dir', check='dir_renamed',
555
path='new-dir', file_id='dir-id')),
557
dict(actions='delete_dir', check='dir_doesnt_exist',
558
# PathConflicts deletion handling requires a special
560
path='<deleted>', file_id='dir-id')),),
561
# Dir renamed/renamed differently
562
(dict(_base_actions='create_dir'),
564
dict(actions='rename_dir', check='dir_renamed',
565
path='new-dir', file_id='dir-id')),
567
dict(actions='rename_dir2', check='dir_renamed2',
568
path='new-dir2', file_id='dir-id')),),
571
def do_create_file(self):
572
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
574
def do_create_dir(self):
575
return [('add', ('dir', 'dir-id', 'directory', ''))]
577
def do_rename_file(self):
578
return [('rename', ('file', 'new-file'))]
580
def check_file_renamed(self):
581
self.assertPathDoesNotExist('branch/file')
582
self.assertPathExists('branch/new-file')
584
def do_rename_file2(self):
585
return [('rename', ('file', 'new-file2'))]
587
def check_file_renamed2(self):
588
self.assertPathDoesNotExist('branch/file')
589
self.assertPathExists('branch/new-file2')
591
def do_rename_dir(self):
592
return [('rename', ('dir', 'new-dir'))]
594
def check_dir_renamed(self):
595
self.assertPathDoesNotExist('branch/dir')
596
self.assertPathExists('branch/new-dir')
598
def do_rename_dir2(self):
599
return [('rename', ('dir', 'new-dir2'))]
601
def check_dir_renamed2(self):
602
self.assertPathDoesNotExist('branch/dir')
603
self.assertPathExists('branch/new-dir2')
605
def do_delete_file(self):
606
return [('unversion', 'file-id')]
608
def check_file_doesnt_exist(self):
609
self.assertPathDoesNotExist('branch/file')
611
def do_delete_dir(self):
612
return [('unversion', 'dir-id')]
614
def check_dir_doesnt_exist(self):
615
self.assertPathDoesNotExist('branch/dir')
617
def do_create_file_in_dir(self):
618
return [('add', ('dir', 'dir-id', 'directory', '')),
619
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
621
def do_rename_file_in_dir(self):
622
return [('rename', ('dir/file', 'dir/new-file'))]
624
def check_file_in_dir_renamed(self):
625
self.assertPathDoesNotExist('branch/dir/file')
626
self.assertPathExists('branch/dir/new-file')
628
def check_file_in_dir_doesnt_exist(self):
629
self.assertPathDoesNotExist('branch/dir/file')
631
def _get_resolve_path_arg(self, wt, action):
632
tpath = self._this['path']
633
opath = self._other['path']
634
if tpath == '<deleted>':
640
def assertPathConflict(self, wt, c):
641
tpath = self._this['path']
642
tfile_id = self._this['file_id']
643
opath = self._other['path']
644
ofile_id = self._other['file_id']
645
self.assertEqual(tfile_id, ofile_id) # Sanity check
646
self.assertEqual(tfile_id, c.file_id)
647
self.assertEqual(tpath, c.path)
648
self.assertEqual(opath, c.conflict_path)
649
_assert_conflict = assertPathConflict
652
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
653
"""Same as TestResolvePathConflict but a specific conflict object.
656
def assertPathConflict(self, c):
657
# We create a conflict object as it was created before the fix and
658
# inject it into the working tree, the test will exercise the
659
# compatibility code.
660
old_c = conflicts.PathConflict('<deleted>', self._item_path,
662
wt.set_conflicts(conflicts.ConflictList([old_c]))
665
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
667
_conflict_type = conflicts.DuplicateEntry
669
scenarios = mirror_scenarios(
671
# File created with different file-ids
672
(dict(_base_actions='nothing'),
674
dict(actions='create_file_a', check='file_content_a',
675
path='file', file_id='file-a-id')),
677
dict(actions='create_file_b', check='file_content_b',
678
path='file', file_id='file-b-id')),),
679
# File created with different file-ids but deleted on one side
680
(dict(_base_actions='create_file_a'),
682
dict(actions='replace_file_a_by_b', check='file_content_b',
683
path='file', file_id='file-b-id')),
685
dict(actions='modify_file_a', check='file_new_content',
686
path='file', file_id='file-a-id')),),
689
def do_nothing(self):
692
def do_create_file_a(self):
693
return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
695
def check_file_content_a(self):
696
self.assertFileEqual('file a content\n', 'branch/file')
698
def do_create_file_b(self):
699
return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
701
def check_file_content_b(self):
702
self.assertFileEqual('file b content\n', 'branch/file')
704
def do_replace_file_a_by_b(self):
705
return [('unversion', 'file-a-id'),
706
('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
708
def do_modify_file_a(self):
709
return [('modify', ('file-a-id', 'new content\n'))]
711
def check_file_new_content(self):
712
self.assertFileEqual('new content\n', 'branch/file')
714
def _get_resolve_path_arg(self, wt, action):
715
return self._this['path']
717
def assertDuplicateEntry(self, wt, c):
718
tpath = self._this['path']
719
tfile_id = self._this['file_id']
720
opath = self._other['path']
721
ofile_id = self._other['file_id']
722
self.assertEqual(tpath, opath) # Sanity check
723
self.assertEqual(tfile_id, c.file_id)
724
self.assertEqual(tpath + '.moved', c.path)
725
self.assertEqual(tpath, c.conflict_path)
726
_assert_conflict = assertDuplicateEntry
305
729
class TestResolveUnversionedParent(TestResolveConflicts):
435
863
def test_keep_them_all(self):
436
864
self.run_script("""
437
865
$ bzr resolve dir
438
$ bzr commit --strict -m 'No more conflicts nor unknown files'
866
2>2 conflicts resolved, 0 remaining
867
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
441
870
def test_adopt_child(self):
442
871
self.run_script("""
443
$ bzr mv dir/file2 file2
872
$ bzr mv -q dir/file2 file2
873
$ bzr rm -q dir --no-backup
445
874
$ bzr resolve dir
446
$ bzr commit --strict -m 'No more conflicts nor unknown files'
875
2>2 conflicts resolved, 0 remaining
876
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
449
879
def test_kill_them_all(self):
450
880
self.run_script("""
881
$ bzr rm -q dir --no-backup
452
882
$ bzr resolve dir
453
$ bzr commit --strict -m 'No more conflicts nor unknown files'
883
2>2 conflicts resolved, 0 remaining
884
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
456
887
def test_resolve_taking_this(self):
457
888
self.run_script("""
458
889
$ bzr resolve --take-this dir
459
$ bzr commit --strict -m 'No more conflicts nor unknown files'
890
2>2 conflicts resolved, 0 remaining
891
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
462
894
def test_resolve_taking_other(self):
463
895
self.run_script("""
464
896
$ bzr resolve --take-other dir
465
$ bzr commit --strict -m 'No more conflicts nor unknown files'
469
class TestResolvePathConflict(TestResolveConflicts):
476
$ bzr commit -m 'Create trunk'
477
$ bzr mv file file-in-trunk
478
$ bzr commit -m 'Renamed to file-in-trunk'
480
$ bzr branch . -r 1 ../branch
482
$ bzr mv file file-in-branch
483
$ bzr commit -m 'Renamed to file-in-branch'
486
2>R file-in-branch => file-in-trunk
487
2>Path conflict: file-in-branch / file-in-trunk
488
2>1 conflicts encountered.
491
def test_keep_source(self):
493
$ bzr resolve file-in-trunk
494
$ bzr commit --strict -m 'No more conflicts nor unknown files'
497
def test_keep_target(self):
499
$ bzr mv file-in-trunk file-in-branch
500
$ bzr resolve file-in-branch
501
$ bzr commit --strict -m 'No more conflicts nor unknown files'
504
def test_resolve_taking_this(self):
506
$ bzr resolve --take-this file-in-branch
507
$ bzr commit --strict -m 'No more conflicts nor unknown files'
510
def test_resolve_taking_other(self):
512
$ bzr resolve --take-other file-in-branch
513
$ bzr commit --strict -m 'No more conflicts nor unknown files'
517
class TestResolveParentLoop(TestResolveConflicts):
524
$ bzr commit -m 'Create trunk'
526
$ bzr commit -m 'Moved dir2 into dir1'
528
$ bzr branch . -r 1 ../branch
531
$ bzr commit -m 'Moved dir1 into dir2'
534
2>Conflict moving dir2/dir1 into dir2. Cancelled move.
535
2>1 conflicts encountered.
538
def test_take_this(self):
541
$ bzr commit --strict -m 'No more conflicts nor unknown files'
544
def test_take_other(self):
546
$ bzr mv dir2/dir1 dir1
549
$ bzr commit --strict -m 'No more conflicts nor unknown files'
552
def test_resolve_taking_this(self):
554
$ bzr resolve --take-this dir2
555
$ bzr commit --strict -m 'No more conflicts nor unknown files'
557
self.failUnlessExists('dir2')
559
def test_resolve_taking_other(self):
561
$ bzr resolve --take-other dir2
562
$ bzr commit --strict -m 'No more conflicts nor unknown files'
564
self.failUnlessExists('dir1')
899
2>2 conflicts resolved, 0 remaining
900
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
904
class TestResolveParentLoop(TestParametrizedResolveConflicts):
906
_conflict_type = conflicts.ParentLoop
911
# Each side dict additionally defines:
912
# - dir_id: the directory being moved
913
# - target_id: The target directory
914
# - xfail: whether the test is expected to fail if the action is
915
# involved as 'other'
916
scenarios = mirror_scenarios(
918
# Dirs moved into each other
919
(dict(_base_actions='create_dir1_dir2'),
921
dict(actions='move_dir1_into_dir2', check='dir1_moved',
922
dir_id='dir1-id', target_id='dir2-id', xfail=False)),
924
dict(actions='move_dir2_into_dir1', check='dir2_moved',
925
dir_id='dir2-id', target_id='dir1-id', xfail=False))),
926
# Subdirs moved into each other
927
(dict(_base_actions='create_dir1_4'),
929
dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
930
dir_id='dir1-id', target_id='dir4-id', xfail=True)),
932
dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
933
dir_id='dir3-id', target_id='dir2-id', xfail=True))),
936
def do_create_dir1_dir2(self):
937
return [('add', ('dir1', 'dir1-id', 'directory', '')),
938
('add', ('dir2', 'dir2-id', 'directory', '')),]
940
def do_move_dir1_into_dir2(self):
941
return [('rename', ('dir1', 'dir2/dir1'))]
943
def check_dir1_moved(self):
944
self.assertPathDoesNotExist('branch/dir1')
945
self.assertPathExists('branch/dir2/dir1')
947
def do_move_dir2_into_dir1(self):
948
return [('rename', ('dir2', 'dir1/dir2'))]
950
def check_dir2_moved(self):
951
self.assertPathDoesNotExist('branch/dir2')
952
self.assertPathExists('branch/dir1/dir2')
954
def do_create_dir1_4(self):
955
return [('add', ('dir1', 'dir1-id', 'directory', '')),
956
('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
957
('add', ('dir3', 'dir3-id', 'directory', '')),
958
('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
960
def do_move_dir1_into_dir4(self):
961
return [('rename', ('dir1', 'dir3/dir4/dir1'))]
963
def check_dir1_2_moved(self):
964
self.assertPathDoesNotExist('branch/dir1')
965
self.assertPathExists('branch/dir3/dir4/dir1')
966
self.assertPathExists('branch/dir3/dir4/dir1/dir2')
968
def do_move_dir3_into_dir2(self):
969
return [('rename', ('dir3', 'dir1/dir2/dir3'))]
971
def check_dir3_4_moved(self):
972
self.assertPathDoesNotExist('branch/dir3')
973
self.assertPathExists('branch/dir1/dir2/dir3')
974
self.assertPathExists('branch/dir1/dir2/dir3/dir4')
976
def _get_resolve_path_arg(self, wt, action):
977
# ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
978
# But since <path> doesn't exist in the working tree, we need to use
979
# <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
980
return wt.id2path(self._other['dir_id'])
982
def assertParentLoop(self, wt, c):
983
self.assertEqual(self._other['dir_id'], c.file_id)
984
self.assertEqual(self._other['target_id'], c.conflict_file_id)
985
# The conflict paths are irrelevant (they are deterministic but not
986
# worth checking since they don't provide the needed information
988
if self._other['xfail']:
989
# It's a bit hackish to raise from here relying on being called for
990
# both tests but this avoid overriding test_resolve_taking_other
992
"ParentLoop doesn't carry enough info to resolve --take-other")
993
_assert_conflict = assertParentLoop
567
996
class TestResolveNonDirectoryParent(TestResolveConflicts):
573
$ bzr commit -m 'Create trunk'
1004
$ bzr commit -m 'Create trunk' -q
574
1005
$ echo "Boing" >foo/bar
576
$ bzr commit -m 'Add foo/bar'
578
$ bzr branch . -r 1 ../branch
1006
$ bzr add -q foo/bar
1007
$ bzr commit -q -m 'Add foo/bar'
1008
$ bzr branch -q . -r 1 ../branch
581
1011
$ echo "Boo!" >foo
582
$ bzr commit -m 'foo is now a file'
1012
$ bzr commit -q -m 'foo is now a file'
584
1013
$ bzr merge ../trunk
585
1014
2>+N foo.new/bar
586
1015
2>RK foo => foo.new/