~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Ian Clatworthy
  • Date: 2010-03-30 20:13:52 UTC
  • mto: This revision was merged to the branch mainline in revision 5125.
  • Revision ID: ian.clatworthy@canonical.com-20100330201352-vw2gtujybyg3rvwc
whitespace fix in win32 installer

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
import os
19
19
 
20
20
from bzrlib import (
 
21
    branchbuilder,
21
22
    bzrdir,
22
23
    conflicts,
23
24
    errors,
24
25
    option,
25
26
    tests,
 
27
    workingtree,
26
28
    )
27
29
from bzrlib.tests import script
28
30
 
29
31
 
 
32
def load_tests(standard_tests, module, loader):
 
33
    result = loader.suiteClass()
 
34
 
 
35
    sp_tests, remaining_tests = tests.split_suite_by_condition(
 
36
        standard_tests, tests.condition_isinstance((
 
37
                TestParametrizedResolveConflicts,
 
38
                )))
 
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)
 
44
 
 
45
    # No parametrization for the remaining tests
 
46
    result.addTests(remaining_tests)
 
47
 
 
48
    return result
 
49
 
 
50
 
30
51
# TODO: Test commit with some added, and added-but-missing files
31
52
# RBC 20060124 is that not tested in test_commit.py ?
32
53
 
69
90
                                  ('hello.sploo.OTHER', 'yellowworld2'),
70
91
                                  ])
71
92
        tree.lock_read()
72
 
        self.assertEqual(6, len(list(tree.list_files())))
 
93
        self.assertLength(6, list(tree.list_files()))
73
94
        tree.unlock()
74
95
        tree_conflicts = tree.conflicts()
75
 
        self.assertEqual(2, len(tree_conflicts))
 
96
        self.assertLength(2, tree_conflicts)
76
97
        self.assertTrue('hello' in tree_conflicts[0].path)
77
98
        self.assertTrue('hello.sploo' in tree_conflicts[1].path)
78
99
        conflicts.restore('hello')
79
100
        conflicts.restore('hello.sploo')
80
 
        self.assertEqual(0, len(tree.conflicts()))
 
101
        self.assertLength(0, tree.conflicts())
81
102
        self.assertFileEqual('hello world2', 'hello')
82
103
        self.assertFalse(os.path.lexists('hello.sploo'))
83
104
        self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
177
198
# FIXME: The shell-like tests should be converted to real whitebox tests... or
178
199
# moved to a blackbox module -- vila 20100205
179
200
 
 
201
# FIXME: test missing for multiple conflicts
 
202
 
180
203
# FIXME: Tests missing for DuplicateID conflict type
181
204
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
182
205
 
192
215
    pass
193
216
 
194
217
 
195
 
class TestResolveContentConflicts(TestResolveConflicts):
196
 
 
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.
199
 
 
200
 
    preamble = """
201
 
$ bzr init trunk
202
 
$ cd trunk
203
 
$ echo 'trunk content' >file
204
 
$ bzr add file
205
 
$ bzr commit -m 'Create trunk'
206
 
 
207
 
$ bzr branch . ../branch
208
 
$ cd ../branch
209
 
$ bzr rm file
210
 
$ bzr commit -m 'Delete file'
211
 
 
212
 
$ cd ../trunk
213
 
$ echo 'more content' >>file
214
 
$ bzr commit -m 'Modify file'
215
 
 
216
 
$ cd ../branch
217
 
$ bzr merge ../trunk
218
 
2>+N  file.OTHER
219
 
2>Contents conflict in file
220
 
2>1 conflicts encountered.
221
 
"""
222
 
 
223
 
    def test_take_this(self):
224
 
        self.run_script("""
225
 
$ bzr rm file.OTHER --force # a simple rm file.OTHER is valid too
226
 
$ bzr resolve file
227
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
228
 
""")
229
 
 
230
 
    def test_take_other(self):
231
 
        self.run_script("""
232
 
$ bzr mv file.OTHER file
233
 
$ bzr resolve file
234
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
235
 
""")
236
 
 
237
 
    def test_resolve_taking_this(self):
238
 
        self.run_script("""
239
 
$ bzr resolve --take-this file
240
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
241
 
""")
242
 
 
243
 
    def test_resolve_taking_other(self):
244
 
        self.run_script("""
245
 
$ bzr resolve --take-other file
246
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
247
 
""")
248
 
 
249
 
 
250
 
class TestResolveDuplicateEntry(TestResolveConflicts):
251
 
 
252
 
    preamble = """
253
 
$ bzr init trunk
254
 
$ cd trunk
255
 
$ echo 'trunk content' >file
256
 
$ bzr add file
257
 
$ bzr commit -m 'Create trunk'
258
 
$ echo 'trunk content too' >file2
259
 
$ bzr add file2
260
 
$ bzr commit -m 'Add file2 in trunk'
261
 
 
262
 
$ bzr branch . -r 1 ../branch
263
 
$ cd ../branch
264
 
$ echo 'branch content' >file2
265
 
$ bzr add file2
266
 
$ bzr commit -m 'Add file2 in branch'
267
 
 
268
 
$ bzr merge ../trunk
269
 
2>+N  file2
270
 
2>R   file2 => file2.moved
271
 
2>Conflict adding file file2.  Moved existing file to file2.moved.
272
 
2>1 conflicts encountered.
273
 
"""
274
 
 
275
 
    def test_keep_this(self):
276
 
        self.run_script("""
277
 
$ bzr rm file2  --force
278
 
$ bzr mv file2.moved file2
279
 
$ bzr resolve file2
280
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
281
 
""")
282
 
 
283
 
    def test_keep_other(self):
284
 
        self.failIfExists('branch/file2.moved')
285
 
        self.run_script("""
286
 
$ bzr rm file2.moved --force
287
 
$ bzr resolve file2
288
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
289
 
""")
290
 
        self.failIfExists('branch/file2.moved')
291
 
 
292
 
    def test_resolve_taking_this(self):
293
 
        self.run_script("""
294
 
$ bzr resolve --take-this file2
295
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
296
 
""")
297
 
 
298
 
    def test_resolve_taking_other(self):
299
 
        self.run_script("""
300
 
$ bzr resolve --take-other file2
301
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
302
 
""")
 
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
 
241
 
 
242
    # Set by load_tests
 
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 []
 
292
 
 
293
    def setUp(self):
 
294
        super(TestParametrizedResolveConflicts, self).setUp()
 
295
        builder = self.make_branch_builder('trunk')
 
296
        builder.start_series()
 
297
 
 
298
        # Create an empty trunk
 
299
        builder.build_snapshot('start', None, [
 
300
                ('add', ('', 'root-id', 'directory', ''))])
 
301
        # Add a minimal base content
 
302
        _, _, actions_base = self._get_actions(self._actions_base)()
 
303
        builder.build_snapshot('base', ['start'], actions_base)
 
304
        # Modify the base content in branch
 
305
        (self._other_path, self._other_id,
 
306
         actions_other) = self._get_actions(self._actions_other)()
 
307
        builder.build_snapshot('other', ['base'], actions_other)
 
308
        # Modify the base content in trunk
 
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
 
 
314
        builder.finish_series()
 
315
        self.builder = builder
 
316
 
 
317
    def _get_actions(self, name):
 
318
        return getattr(self, 'do_%s' % name)
 
319
 
 
320
    def _get_check(self, name):
 
321
        return getattr(self, 'check_%s' % name)
 
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
 
 
347
    def do_modify_file(self):
 
348
        return ('file', 'file-id',
 
349
                [('modify', ('file-id', 'trunk content\nmore content\n'))])
 
350
 
 
351
    def check_file_has_more_content(self):
 
352
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
 
353
 
 
354
    def do_delete_file(self):
 
355
        return ('file', 'file-id', [('unversion', 'file-id')])
 
356
 
 
357
    def check_file_doesnt_exist(self):
 
358
        self.failIfExists('branch/file')
 
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
 
 
394
    def _merge_other_into_this(self):
 
395
        b = self.builder.get_branch()
 
396
        wt = b.bzrdir.sprout('branch').open_workingtree()
 
397
        wt.merge_from_branch(b, 'other')
 
398
        return wt
 
399
 
 
400
    def assertConflict(self, wt):
 
401
        confs = wt.conflicts()
 
402
        self.assertLength(1, confs)
 
403
        c = confs[0]
 
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)
 
413
        # Check that we don't have any conflicts nor unknown left
 
414
        self.assertLength(0, wt.conflicts())
 
415
        self.assertLength(0, list(wt.unknowns()))
 
416
 
 
417
    def test_resolve_taking_this(self):
 
418
        wt = self._merge_other_into_this()
 
419
        self.assertConflict(wt)
 
420
        self.check_resolved(wt, 'take_this')
 
421
        check_this = self._get_check(self._check_this)
 
422
        check_this()
 
423
 
 
424
    def test_resolve_taking_other(self):
 
425
        wt = self._merge_other_into_this()
 
426
        self.assertConflict(wt)
 
427
        self.check_resolved(wt, 'take_other')
 
428
        check_other = self._get_check(self._check_other)
 
429
        check_other()
 
430
 
 
431
 
 
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
303
532
 
304
533
 
305
534
class TestResolveUnversionedParent(TestResolveConflicts):
314
543
$ mkdir dir
315
544
$ bzr add dir
316
545
$ bzr commit -m 'Create trunk'
 
546
 
317
547
$ echo 'trunk content' >dir/file
318
548
$ bzr add dir/file
319
549
$ bzr commit -m 'Add dir/file in trunk'
354
584
$ echo 'trunk content' >dir/file
355
585
$ bzr add
356
586
$ bzr commit -m 'Create trunk'
 
587
 
357
588
$ echo 'trunk content' >dir/file2
358
589
$ bzr add dir/file2
359
590
$ bzr commit -m 'Add dir/file2 in branch'
415
646
$ echo 'trunk content' >dir/file
416
647
$ bzr add
417
648
$ bzr commit -m 'Create trunk'
 
649
 
418
650
$ bzr rm dir/file --force
419
651
$ bzr rm dir --force
420
652
$ bzr commit -m 'Remove dir/file'
466
698
""")
467
699
 
468
700
 
469
 
class TestResolvePathConflict(TestResolveConflicts):
470
 
 
471
 
    preamble = """
472
 
$ bzr init trunk
473
 
$ cd trunk
474
 
$ echo 'Boo!' >file
475
 
$ bzr add
476
 
$ bzr commit -m 'Create trunk'
477
 
$ bzr mv file file-in-trunk
478
 
$ bzr commit -m 'Renamed to file-in-trunk'
479
 
 
480
 
$ bzr branch . -r 1 ../branch
481
 
$ cd ../branch
482
 
$ bzr mv file file-in-branch
483
 
$ bzr commit -m 'Renamed to file-in-branch'
484
 
 
485
 
$ bzr merge ../trunk
486
 
2>R   file-in-branch => file-in-trunk
487
 
2>Path conflict: file-in-branch / file-in-trunk
488
 
2>1 conflicts encountered.
489
 
"""
490
 
 
491
 
    def test_keep_source(self):
492
 
        self.run_script("""
493
 
$ bzr resolve file-in-trunk
494
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
495
 
""")
496
 
 
497
 
    def test_keep_target(self):
498
 
        self.run_script("""
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'
502
 
""")
503
 
 
504
 
    def test_resolve_taking_this(self):
505
 
        self.run_script("""
506
 
$ bzr resolve --take-this file-in-branch
507
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
508
 
""")
509
 
 
510
 
    def test_resolve_taking_other(self):
511
 
        self.run_script("""
512
 
$ bzr resolve --take-other file-in-branch
513
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
514
 
""")
515
 
 
516
 
 
517
 
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):
518
796
 
519
797
    preamble = """
520
798
$ bzr init trunk
522
800
$ bzr mkdir dir1
523
801
$ bzr mkdir dir2
524
802
$ bzr commit -m 'Create trunk'
 
803
 
525
804
$ bzr mv dir2 dir1
526
805
$ bzr commit -m 'Moved dir2 into dir1'
527
806
 
531
810
$ bzr commit -m 'Moved dir1 into dir2'
532
811
 
533
812
$ bzr merge ../trunk
534
 
2>Conflict moving dir2/dir1 into dir2.  Cancelled move.
 
813
2>Conflict moving dir2 into dir2/dir1. Cancelled move.
535
814
2>1 conflicts encountered.
536
815
"""
537
816