/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Andrew Bennetts
  • Date: 2010-03-26 04:47:45 UTC
  • mfrom: (5116 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5117.
  • Revision ID: andrew.bennetts@canonical.com-20100326044745-ubvt5tmse1a17s1f
MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
 
35
35
    sp_tests, remaining_tests = tests.split_suite_by_condition(
36
36
        standard_tests, tests.condition_isinstance((
37
 
                TestResolveContentConflicts,
 
37
                TestParametrizedResolveConflicts,
38
38
                )))
39
 
    tests.multiply_tests(sp_tests, content_conflict_scenarios(), result)
 
39
    # Each test class defines its own scenarios. This is needed for
 
40
    # TestResolvePathConflictBefore531967 that verifies that the same tests as
 
41
    # TestResolvePathConflict still pass.
 
42
    for test in tests.iter_suite_tests(sp_tests):
 
43
        tests.apply_scenarios(test, test.scenarios(), result)
40
44
 
41
45
    # No parametrization for the remaining tests
42
46
    result.addTests(remaining_tests)
194
198
# FIXME: The shell-like tests should be converted to real whitebox tests... or
195
199
# moved to a blackbox module -- vila 20100205
196
200
 
 
201
# FIXME: test missing for multiple conflicts
 
202
 
197
203
# FIXME: Tests missing for DuplicateID conflict type
198
204
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
199
205
 
209
215
    pass
210
216
 
211
217
 
212
 
def content_conflict_scenarios():
213
 
    return [('file,None', dict(_this_actions='modify_file',
214
 
                               _check_this='file_has_more_content',
215
 
                               _other_actions='delete_file',
216
 
                               _check_other='file_doesnt_exist',
217
 
                               )),
218
 
            ('None,file', dict(_this_actions='delete_file',
219
 
                               _check_this='file_doesnt_exist',
220
 
                               _other_actions='modify_file',
221
 
                               _check_other='file_has_more_content',
222
 
                               )),
223
 
            ]
224
 
 
225
 
 
226
 
class TestResolveContentConflicts(tests.TestCaseWithTransport):
 
218
# FIXME: Get rid of parametrized (in the class name) once we delete
 
219
# TestResolveConflicts -- vila 20100308
 
220
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
 
221
    """This class provides a base to test single conflict resolution.
 
222
 
 
223
    The aim is to define scenarios in daughter classes (one for each conflict
 
224
    type) that create a single conflict object when one branch is merged in
 
225
    another (and vice versa). Each class can define as many scenarios as
 
226
    needed. Each scenario should define a couple of actions that will be
 
227
    swapped to define the sibling scenarios.
 
228
 
 
229
    From there, both resolutions are tested (--take-this and --take-other).
 
230
 
 
231
    Each conflict type use its attributes in a specific way, so each class 
 
232
    should define a specific _assert_conflict method.
 
233
 
 
234
    Since the resolution change the working tree state, each action should
 
235
    define an associated check.
 
236
    """
 
237
 
 
238
    # Set by daughter classes
 
239
    _conflict_type = None
 
240
    _assert_conflict = None
227
241
 
228
242
    # Set by load_tests
229
 
    this_actions = None
230
 
    other_actions = None
 
243
    _base_actions = None
 
244
    _this_actions = None
 
245
    _other_actions = None
 
246
    _item_path = None
 
247
    _item_id = None
 
248
 
 
249
    # Set by _this_actions and other_actions
 
250
    # FIXME: rename them this_args and other_args so the tests can use them
 
251
    # more freely
 
252
    _this_path = None
 
253
    _this_id = None
 
254
    _other_path = None
 
255
    _other_id = None
 
256
 
 
257
    @classmethod
 
258
    def mirror_scenarios(klass, base_scenarios):
 
259
        scenarios = []
 
260
        def adapt(d, side):
 
261
            """Modify dict to apply to the given side.
 
262
 
 
263
            'actions' key is turned into '_actions_this' if side is 'this' for
 
264
            example.
 
265
            """
 
266
            t = {}
 
267
            # Turn each key into _side_key
 
268
            for k,v in d.iteritems():
 
269
                t['_%s_%s' % (k, side)] = v
 
270
            return t
 
271
        # Each base scenario is duplicated switching the roles of 'this' and
 
272
        # 'other'
 
273
        left = [l for l, r, c in base_scenarios]
 
274
        right = [r for l, r, c in base_scenarios]
 
275
        common = [c for l, r, c in base_scenarios]
 
276
        for (lname, ldict), (rname, rdict), common in zip(left, right, common):
 
277
            a = tests.multiply_scenarios([(lname, adapt(ldict, 'this'))],
 
278
                                         [(rname, adapt(rdict, 'other'))])
 
279
            b = tests.multiply_scenarios(
 
280
                    [(rname, adapt(rdict, 'this'))],
 
281
                    [(lname, adapt(ldict, 'other'))])
 
282
            # Inject the common parameters in all scenarios
 
283
            for name, d in a + b:
 
284
                d.update(common)
 
285
            scenarios.extend(a + b)
 
286
        return scenarios
 
287
 
 
288
    @classmethod
 
289
    def scenarios(klass):
 
290
        # Only concrete classes return actual scenarios
 
291
        return []
231
292
 
232
293
    def setUp(self):
233
 
        super(TestResolveContentConflicts, self).setUp()
 
294
        super(TestParametrizedResolveConflicts, self).setUp()
234
295
        builder = self.make_branch_builder('trunk')
235
296
        builder.start_series()
 
297
 
236
298
        # Create an empty trunk
237
299
        builder.build_snapshot('start', None, [
238
300
                ('add', ('', 'root-id', 'directory', ''))])
239
301
        # Add a minimal base content
240
 
        builder.build_snapshot('base', ['start'], [
241
 
                ('add', ('file', 'file-id', 'file', 'trunk content\n'))])
 
302
        _, _, actions_base = self._get_actions(self._actions_base)()
 
303
        builder.build_snapshot('base', ['start'], actions_base)
242
304
        # Modify the base content in branch
243
 
        other_actions = self._get_actions(self._other_actions)
244
 
        builder.build_snapshot('other', ['base'], other_actions())
 
305
        (self._other_path, self._other_id,
 
306
         actions_other) = self._get_actions(self._actions_other)()
 
307
        builder.build_snapshot('other', ['base'], actions_other)
245
308
        # Modify the base content in trunk
246
 
        this_actions = self._get_actions(self._this_actions)
247
 
        builder.build_snapshot('this', ['base'], this_actions())
 
309
        (self._this_path, self._this_id,
 
310
         actions_this) = self._get_actions(self._actions_this)()
 
311
        builder.build_snapshot('this', ['base'], actions_this)
 
312
        # builder.get_branch() tip is now 'this'
 
313
 
248
314
        builder.finish_series()
249
315
        self.builder = builder
250
316
 
254
320
    def _get_check(self, name):
255
321
        return getattr(self, 'check_%s' % name)
256
322
 
 
323
    def do_nothing(self):
 
324
        return (None, None, [])
 
325
 
 
326
    def do_create_file(self):
 
327
        return ('file', 'file-id',
 
328
                [('add', ('file', 'file-id', 'file', 'trunk content\n'))])
 
329
 
 
330
    def do_create_file_a(self):
 
331
        return ('file', 'file-a-id',
 
332
                [('add', ('file', 'file-a-id', 'file', 'file a content\n'))])
 
333
 
 
334
    def check_file_content_a(self):
 
335
        self.assertFileEqual('file a content\n', 'branch/file')
 
336
 
 
337
    def do_create_file_b(self):
 
338
        return ('file', 'file-b-id',
 
339
                [('add', ('file', 'file-b-id', 'file', 'file b content\n'))])
 
340
 
 
341
    def check_file_content_b(self):
 
342
        self.assertFileEqual('file b content\n', 'branch/file')
 
343
 
 
344
    def do_create_dir(self):
 
345
        return ('dir', 'dir-id', [('add', ('dir', 'dir-id', 'directory', ''))])
 
346
 
257
347
    def do_modify_file(self):
258
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
348
        return ('file', 'file-id',
 
349
                [('modify', ('file-id', 'trunk content\nmore content\n'))])
259
350
 
260
351
    def check_file_has_more_content(self):
261
352
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
262
353
 
263
354
    def do_delete_file(self):
264
 
        return [('unversion', 'file-id')]
 
355
        return ('file', 'file-id', [('unversion', 'file-id')])
265
356
 
266
357
    def check_file_doesnt_exist(self):
267
358
        self.failIfExists('branch/file')
268
359
 
 
360
    def do_rename_file(self):
 
361
        return ('new-file', 'file-id', [('rename', ('file', 'new-file'))])
 
362
 
 
363
    def check_file_renamed(self):
 
364
        self.failIfExists('branch/file')
 
365
        self.failUnlessExists('branch/new-file')
 
366
 
 
367
    def do_rename_file2(self):
 
368
        return ('new-file2', 'file-id', [('rename', ('file', 'new-file2'))])
 
369
 
 
370
    def check_file_renamed2(self):
 
371
        self.failIfExists('branch/file')
 
372
        self.failUnlessExists('branch/new-file2')
 
373
 
 
374
    def do_rename_dir(self):
 
375
        return ('new-dir', 'dir-id', [('rename', ('dir', 'new-dir'))])
 
376
 
 
377
    def check_dir_renamed(self):
 
378
        self.failIfExists('branch/dir')
 
379
        self.failUnlessExists('branch/new-dir')
 
380
 
 
381
    def do_rename_dir2(self):
 
382
        return ('new-dir2', 'dir-id', [('rename', ('dir', 'new-dir2'))])
 
383
 
 
384
    def check_dir_renamed2(self):
 
385
        self.failIfExists('branch/dir')
 
386
        self.failUnlessExists('branch/new-dir2')
 
387
 
 
388
    def do_delete_dir(self):
 
389
        return ('<deleted>', 'dir-id', [('unversion', 'dir-id')])
 
390
 
 
391
    def check_dir_doesnt_exist(self):
 
392
        self.failIfExists('branch/dir')
 
393
 
269
394
    def _merge_other_into_this(self):
270
395
        b = self.builder.get_branch()
271
396
        wt = b.bzrdir.sprout('branch').open_workingtree()
272
397
        wt.merge_from_branch(b, 'other')
273
398
        return wt
274
399
 
275
 
    def assertConflict(self, wt, ctype, **kwargs):
 
400
    def assertConflict(self, wt):
276
401
        confs = wt.conflicts()
277
402
        self.assertLength(1, confs)
278
403
        c = confs[0]
279
 
        self.assertIsInstance(c, ctype)
280
 
        sentinel = object() # An impossible value
281
 
        for k, v in kwargs.iteritems():
282
 
            self.assertEqual(v, getattr(c, k, sentinel))
283
 
 
284
 
    def check_resolved(self, wt, item, action):
285
 
        conflicts.resolve(wt, [item], action=action)
 
404
        self.assertIsInstance(c, self._conflict_type)
 
405
        self._assert_conflict(wt, c)
 
406
 
 
407
    def _get_resolve_path_arg(self, wt, action):
 
408
        return self._item_path
 
409
 
 
410
    def check_resolved(self, wt, action):
 
411
        path = self._get_resolve_path_arg(wt, action)
 
412
        conflicts.resolve(wt, [path], action=action)
286
413
        # Check that we don't have any conflicts nor unknown left
287
414
        self.assertLength(0, wt.conflicts())
288
415
        self.assertLength(0, list(wt.unknowns()))
289
416
 
290
417
    def test_resolve_taking_this(self):
291
418
        wt = self._merge_other_into_this()
292
 
        self.assertConflict(wt, conflicts.ContentsConflict,
293
 
                            path='file', file_id='file-id',)
294
 
        self.check_resolved(wt, 'file', 'take_this')
 
419
        self.assertConflict(wt)
 
420
        self.check_resolved(wt, 'take_this')
295
421
        check_this = self._get_check(self._check_this)
296
422
        check_this()
297
423
 
298
424
    def test_resolve_taking_other(self):
299
425
        wt = self._merge_other_into_this()
300
 
        self.assertConflict(wt, conflicts.ContentsConflict,
301
 
                            path='file', file_id='file-id',)
302
 
        self.check_resolved(wt, 'file', 'take_other')
 
426
        self.assertConflict(wt)
 
427
        self.check_resolved(wt, 'take_other')
303
428
        check_other = self._get_check(self._check_other)
304
429
        check_other()
305
430
 
306
431
 
307
 
class TestResolveDuplicateEntry(TestResolveConflicts):
308
 
 
309
 
    preamble = """
310
 
$ bzr init trunk
311
 
$ cd trunk
312
 
$ echo 'trunk content' >file
313
 
$ bzr add file
314
 
$ bzr commit -m 'Create trunk'
315
 
 
316
 
$ echo 'trunk content too' >file2
317
 
$ bzr add file2
318
 
$ bzr commit -m 'Add file2 in trunk'
319
 
 
320
 
$ bzr branch . -r 1 ../branch
321
 
$ cd ../branch
322
 
$ echo 'branch content' >file2
323
 
$ bzr add file2
324
 
$ bzr commit -m 'Add file2 in branch'
325
 
 
326
 
$ bzr merge ../trunk
327
 
2>+N  file2
328
 
2>R   file2 => file2.moved
329
 
2>Conflict adding file file2.  Moved existing file to file2.moved.
330
 
2>1 conflicts encountered.
331
 
"""
332
 
 
333
 
    def test_keep_this(self):
334
 
        self.run_script("""
335
 
$ bzr rm file2  --force
336
 
$ bzr mv file2.moved file2
337
 
$ bzr resolve file2
338
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
339
 
""")
340
 
 
341
 
    def test_keep_other(self):
342
 
        self.failIfExists('branch/file2.moved')
343
 
        self.run_script("""
344
 
$ bzr rm file2.moved --force
345
 
$ bzr resolve file2
346
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
347
 
""")
348
 
        self.failIfExists('branch/file2.moved')
349
 
 
350
 
    def test_resolve_taking_this(self):
351
 
        self.run_script("""
352
 
$ bzr resolve --take-this file2
353
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
354
 
""")
355
 
 
356
 
    def test_resolve_taking_other(self):
357
 
        self.run_script("""
358
 
$ bzr resolve --take-other file2
359
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
360
 
""")
 
432
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
 
433
 
 
434
    _conflict_type = conflicts.ContentsConflict,
 
435
    @classmethod
 
436
    def scenarios(klass):
 
437
        base_scenarios = [
 
438
            (('file_modified', dict(actions='modify_file',
 
439
                                   check='file_has_more_content')),
 
440
             ('file_deleted', dict(actions='delete_file',
 
441
                                   check='file_doesnt_exist')),
 
442
             dict(_actions_base='create_file', _item_path='file')),
 
443
            ]
 
444
        return klass.mirror_scenarios(base_scenarios)
 
445
 
 
446
    def assertContentsConflict(self, wt, c):
 
447
        self.assertEqual(self._other_id, c.file_id)
 
448
        self.assertEqual(self._other_path, c.path)
 
449
    _assert_conflict = assertContentsConflict
 
450
 
 
451
 
 
452
 
 
453
class TestResolvePathConflict(TestParametrizedResolveConflicts):
 
454
 
 
455
    _conflict_type = conflicts.PathConflict,
 
456
 
 
457
    @classmethod
 
458
    def scenarios(klass):
 
459
        for_file = dict(_actions_base='create_file',
 
460
                  _item_path='new-file', _item_id='file-id',)
 
461
        for_dir = dict(_actions_base='create_dir',
 
462
                        _item_path='new-dir', _item_id='dir-id',)
 
463
        base_scenarios = [
 
464
            (('file_renamed',
 
465
              dict(actions='rename_file', check='file_renamed')),
 
466
             ('file_deleted',
 
467
              dict(actions='delete_file', check='file_doesnt_exist')),
 
468
             for_file),
 
469
            (('file_renamed',
 
470
              dict(actions='rename_file', check='file_renamed')),
 
471
             ('file_renamed2',
 
472
              dict(actions='rename_file2', check='file_renamed2')),
 
473
             for_file),
 
474
            (('dir_renamed',
 
475
              dict(actions='rename_dir', check='dir_renamed')),
 
476
             ('dir_deleted',
 
477
              dict(actions='delete_dir', check='dir_doesnt_exist')),
 
478
             for_dir),
 
479
            (('dir_renamed',
 
480
              dict(actions='rename_dir', check='dir_renamed')),
 
481
             ('dir_renamed2',
 
482
              dict(actions='rename_dir2', check='dir_renamed2')),
 
483
             for_dir),
 
484
        ]
 
485
        return klass.mirror_scenarios(base_scenarios)
 
486
 
 
487
    def do_delete_file(self):
 
488
        sup = super(TestResolvePathConflict, self).do_delete_file()
 
489
        # PathConflicts handle deletion differently and requires a special
 
490
        # hard-coded value
 
491
        return ('<deleted>',) + sup[1:]
 
492
 
 
493
    def assertPathConflict(self, wt, c):
 
494
        self.assertEqual(self._item_id, c.file_id)
 
495
        self.assertEqual(self._this_path, c.path)
 
496
        self.assertEqual(self._other_path, c.conflict_path)
 
497
    _assert_conflict = assertPathConflict
 
498
 
 
499
 
 
500
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
 
501
    """Same as TestResolvePathConflict but a specific conflict object.
 
502
    """
 
503
 
 
504
    def assertPathConflict(self, c):
 
505
        # We create a conflict object as it was created before the fix and
 
506
        # inject it into the working tree, the test will exercise the
 
507
        # compatibility code.
 
508
        old_c = conflicts.PathConflict('<deleted>', self._item_path,
 
509
                                       file_id=None)
 
510
        wt.set_conflicts(conflicts.ConflictList([old_c]))
 
511
 
 
512
 
 
513
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
 
514
 
 
515
    _conflict_type = conflicts.DuplicateEntry,
 
516
    @classmethod
 
517
    def scenarios(klass):
 
518
        base_scenarios = [
 
519
            (('filea_created', dict(actions='create_file_a',
 
520
                                    check='file_content_a')),
 
521
             ('fileb_created', dict(actions='create_file_b',
 
522
                                   check='file_content_b')),
 
523
             dict(_actions_base='nothing', _item_path='file')),
 
524
            ]
 
525
        return klass.mirror_scenarios(base_scenarios)
 
526
 
 
527
    def assertDuplicateEntry(self, wt, c):
 
528
        self.assertEqual(self._this_id, c.file_id)
 
529
        self.assertEqual(self._item_path + '.moved', c.path)
 
530
        self.assertEqual(self._item_path, c.conflict_path)
 
531
    _assert_conflict = assertDuplicateEntry
361
532
 
362
533
 
363
534
class TestResolveUnversionedParent(TestResolveConflicts):
527
698
""")
528
699
 
529
700
 
530
 
class TestResolvePathConflict(TestResolveConflicts):
531
 
 
532
 
    preamble = """
533
 
$ bzr init trunk
534
 
$ cd trunk
535
 
$ echo 'Boo!' >file
536
 
$ bzr add
537
 
$ bzr commit -m 'Create trunk'
538
 
 
539
 
$ bzr mv file file-in-trunk
540
 
$ bzr commit -m 'Renamed to file-in-trunk'
541
 
 
542
 
$ bzr branch . -r 1 ../branch
543
 
$ cd ../branch
544
 
$ bzr mv file file-in-branch
545
 
$ bzr commit -m 'Renamed to file-in-branch'
546
 
 
547
 
$ bzr merge ../trunk
548
 
2>R   file-in-branch => file-in-trunk
549
 
2>Path conflict: file-in-branch / file-in-trunk
550
 
2>1 conflicts encountered.
551
 
"""
552
 
 
553
 
    def test_keep_source(self):
554
 
        self.run_script("""
555
 
$ bzr resolve file-in-trunk
556
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
557
 
""")
558
 
 
559
 
    def test_keep_target(self):
560
 
        self.run_script("""
561
 
$ bzr mv file-in-trunk file-in-branch
562
 
$ bzr resolve file-in-branch
563
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
564
 
""")
565
 
 
566
 
    def test_resolve_taking_this(self):
567
 
        self.run_script("""
568
 
$ bzr resolve --take-this file-in-branch
569
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
570
 
""")
571
 
 
572
 
    def test_resolve_taking_other(self):
573
 
        self.run_script("""
574
 
$ bzr resolve --take-other file-in-branch
575
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
576
 
""")
577
 
 
578
 
 
579
 
class TestResolveParentLoop(TestResolveConflicts):
 
701
class TestResolveParentLoop(TestParametrizedResolveConflicts):
 
702
 
 
703
    _conflict_type = conflicts.ParentLoop,
 
704
    @classmethod
 
705
    def scenarios(klass):
 
706
        base_scenarios = [
 
707
            (('dir1_into_dir2', dict(actions='move_dir1_into_dir2',
 
708
                                      check='dir1_moved')),
 
709
             ('dir2_into_dir1', dict(actions='move_dir2_into_dir1',
 
710
                                      check='dir2_moved')),
 
711
             dict(_actions_base='create_dir1_dir2')),
 
712
            (('dir1_into_dir4', dict(actions='move_dir1_into_dir4',
 
713
                                      check='dir1_2_moved')),
 
714
             ('dir3_into_dir2', dict(actions='move_dir3_into_dir2',
 
715
                                      check='dir3_4_moved')),
 
716
             dict(_actions_base='create_dir1_4')),
 
717
            ]
 
718
        return klass.mirror_scenarios(base_scenarios)
 
719
 
 
720
    def do_create_dir1_dir2(self):
 
721
        return (None, None,
 
722
                [('add', ('dir1', 'dir1-id', 'directory', '')),
 
723
                 ('add', ('dir2', 'dir2-id', 'directory', '')),
 
724
                 ])
 
725
 
 
726
    def do_move_dir1_into_dir2(self):
 
727
        # The arguments are the file-id to move and the targeted file-id dir.
 
728
        return ('dir1-id', 'dir2-id', [('rename', ('dir1', 'dir2/dir1'))])
 
729
 
 
730
    def check_dir1_moved(self):
 
731
        self.failIfExists('branch/dir1')
 
732
        self.failUnlessExists('branch/dir2/dir1')
 
733
 
 
734
    def do_move_dir2_into_dir1(self):
 
735
        # The arguments are the file-id to move and the targeted file-id dir.
 
736
        return ('dir2-id', 'dir1-id', [('rename', ('dir2', 'dir1/dir2'))])
 
737
 
 
738
    def check_dir2_moved(self):
 
739
        self.failIfExists('branch/dir2')
 
740
        self.failUnlessExists('branch/dir1/dir2')
 
741
 
 
742
    def do_create_dir1_4(self):
 
743
        return (None, None,
 
744
                [('add', ('dir1', 'dir1-id', 'directory', '')),
 
745
                 ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
 
746
                 ('add', ('dir3', 'dir3-id', 'directory', '')),
 
747
                 ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),
 
748
                 ])
 
749
 
 
750
    def do_move_dir1_into_dir4(self):
 
751
        # The arguments are the file-id to move and the targeted file-id dir.
 
752
        return ('dir1-id', 'dir4-id',
 
753
                [('rename', ('dir1', 'dir3/dir4/dir1'))])
 
754
 
 
755
    def check_dir1_2_moved(self):
 
756
        self.failIfExists('branch/dir1')
 
757
        self.failUnlessExists('branch/dir3/dir4/dir1')
 
758
        self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
 
759
 
 
760
    def do_move_dir3_into_dir2(self):
 
761
        # The arguments are the file-id to move and the targeted file-id dir.
 
762
        return ('dir3-id', 'dir2-id',
 
763
                [('rename', ('dir3', 'dir1/dir2/dir3'))])
 
764
 
 
765
    def check_dir3_4_moved(self):
 
766
        self.failIfExists('branch/dir3')
 
767
        self.failUnlessExists('branch/dir1/dir2/dir3')
 
768
        self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
 
769
 
 
770
    def _get_resolve_path_arg(self, wt, action):
 
771
        # ParentLoop is unsual as it says: 
 
772
        # moving <conflict_path> into <path>.  Cancelled move.
 
773
        # But since <path> doesn't exist in the working tree, we need to use
 
774
        # <conflict_path> instead
 
775
        path = wt.id2path(self._other_id)
 
776
        return path
 
777
 
 
778
    def assertParentLoop(self, wt, c):
 
779
        if 'taking_other(' in self.id() and 'dir4' in self.id():
 
780
            raise tests.KnownFailure(
 
781
                "ParentLoop doesn't carry enough info to resolve")
 
782
        # The relevant file-ids are other_args swapped (which is the main
 
783
        # reason why they should be renamed other_args instead of Other_path
 
784
        # and other_id). In the conflict object, they represent:
 
785
        # c.file_id: the directory being moved
 
786
        # c.conflict_id_id: The target directory
 
787
        self.assertEqual(self._other_path, c.file_id)
 
788
        self.assertEqual(self._other_id, c.conflict_file_id)
 
789
        # The conflict paths are irrelevant (they are deterministic but not
 
790
        # worth checking since they don't provide the needed information
 
791
        # anyway)
 
792
    _assert_conflict = assertParentLoop
 
793
 
 
794
 
 
795
class OldTestResolveParentLoop(TestResolveConflicts):
580
796
 
581
797
    preamble = """
582
798
$ bzr init trunk
594
810
$ bzr commit -m 'Moved dir1 into dir2'
595
811
 
596
812
$ bzr merge ../trunk
597
 
2>Conflict moving dir2/dir1 into dir2.  Cancelled move.
 
813
2>Conflict moving dir2 into dir2/dir1. Cancelled move.
598
814
2>1 conflicts encountered.
599
815
"""
600
816