/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-04-08 07:01:10 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5141.
  • Revision ID: andrew.bennetts@canonical.com-20100408070110-mnvv0kbbyaj6cqdg
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
def mirror_scenarios(base_scenarios):
 
219
    """Return a list of mirrored scenarios.
 
220
 
 
221
    Each scenario in base_scenarios is duplicated switching the roles of 'this'
 
222
    and 'other'
 
223
    """
 
224
    scenarios = []
 
225
    for common, (lname, ldict), (rname, rdict) in base_scenarios:
 
226
        a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
 
227
                                     [(rname, dict(_other=rdict))])
 
228
        b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
 
229
                                     [(lname, dict(_other=ldict))])
 
230
        # Inject the common parameters in all scenarios
 
231
        for name, d in a + b:
 
232
            d.update(common)
 
233
        scenarios.extend(a + b)
 
234
    return scenarios
 
235
 
 
236
 
 
237
# FIXME: Get rid of parametrized (in the class name) once we delete
 
238
# TestResolveConflicts -- vila 20100308
 
239
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
 
240
    """This class provides a base to test single conflict resolution.
 
241
 
 
242
    Since all conflict objects are created with specific semantics for their
 
243
    attributes, each class should implement the necessary functions and
 
244
    attributes described below.
 
245
 
 
246
    Each class should define the scenarios that create the expected (single)
 
247
    conflict.
 
248
 
 
249
    Each scenario describes:
 
250
    * how to create 'base' tree (and revision)
 
251
    * how to create 'left' tree (and revision, parent rev 'base')
 
252
    * how to create 'right' tree (and revision, parent rev 'base')
 
253
    * how to check that changes in 'base'->'left' have been taken
 
254
    * how to check that changes in 'base'->'right' have been taken
 
255
 
 
256
    From each base scenario, we generate two concrete scenarios where:
 
257
    * this=left, other=right
 
258
    * this=right, other=left
 
259
 
 
260
    Then the test case verifies each concrete scenario by:
 
261
    * creating a branch containing the 'base', 'this' and 'other' revisions
 
262
    * creating a working tree for the 'this' revision
 
263
    * performing the merge of 'other' into 'this'
 
264
    * verifying the expected conflict was generated
 
265
    * resolving with --take-this or --take-other, and running the corresponding
 
266
      checks (for either 'base'->'this', or 'base'->'other')
 
267
 
 
268
    :cvar _conflict_type: The expected class of the generated conflict.
 
269
 
 
270
    :cvar _assert_conflict: A method receiving the working tree and the
 
271
        conflict object and checking its attributes.
 
272
 
 
273
    :cvar _base_actions: The branchbuilder actions to create the 'base'
 
274
        revision.
 
275
 
 
276
    :cvar _this: The dict related to 'base' -> 'this'. It contains at least:
 
277
      * 'actions': The branchbuilder actions to create the 'this'
 
278
          revision.
 
279
      * 'check': how to check the changes after resolution with --take-this.
 
280
 
 
281
    :cvar _other: The dict related to 'base' -> 'other'. It contains at least:
 
282
      * 'actions': The branchbuilder actions to create the 'other'
 
283
          revision.
 
284
      * 'check': how to check the changes after resolution with --take-other.
 
285
    """
 
286
 
 
287
    # Set by daughter classes
 
288
    _conflict_type = None
 
289
    _assert_conflict = None
227
290
 
228
291
    # Set by load_tests
229
 
    this_actions = None
230
 
    other_actions = None
 
292
    _base_actions = None
 
293
    _this = None
 
294
    _other = None
 
295
 
 
296
    @staticmethod
 
297
    def scenarios():
 
298
        """Return the scenario list for the conflict type defined by the class.
 
299
 
 
300
        Each scenario is of the form:
 
301
        (common, (left_name, left_dict), (right_name, right_dict))
 
302
 
 
303
        * common is a dict
 
304
 
 
305
        * left_name and right_name are the scenario names that will be combined
 
306
 
 
307
        * left_dict and right_dict are the attributes specific to each half of
 
308
          the scenario. They should include at least 'actions' and 'check' and
 
309
          will be available as '_this' and '_other' test instance attributes.
 
310
 
 
311
        Daughters classes are free to add their specific attributes as they see
 
312
        fit in any of the three dicts.
 
313
 
 
314
        This is a class method so that load_tests can find it.
 
315
 
 
316
        '_base_actions' in the common dict, 'actions' and 'check' in the left
 
317
        and right dicts use names that map to methods in the test classes. Some
 
318
        prefixes are added to these names to get the correspong methods (see
 
319
        _get_actions() and _get_check()). The motivation here is to avoid
 
320
        collisions in the class namespace.
 
321
        """
 
322
        # Only concrete classes return actual scenarios
 
323
        return []
231
324
 
232
325
    def setUp(self):
233
 
        super(TestResolveContentConflicts, self).setUp()
 
326
        super(TestParametrizedResolveConflicts, self).setUp()
234
327
        builder = self.make_branch_builder('trunk')
235
328
        builder.start_series()
 
329
 
236
330
        # Create an empty trunk
237
331
        builder.build_snapshot('start', None, [
238
332
                ('add', ('', 'root-id', 'directory', ''))])
239
333
        # Add a minimal base content
240
 
        builder.build_snapshot('base', ['start'], [
241
 
                ('add', ('file', 'file-id', 'file', 'trunk content\n'))])
 
334
        base_actions = self._get_actions(self._base_actions)()
 
335
        builder.build_snapshot('base', ['start'], base_actions)
242
336
        # Modify the base content in branch
243
 
        other_actions = self._get_actions(self._other_actions)
244
 
        builder.build_snapshot('other', ['base'], other_actions())
 
337
        actions_other = self._get_actions(self._other['actions'])()
 
338
        builder.build_snapshot('other', ['base'], actions_other)
245
339
        # Modify the base content in trunk
246
 
        this_actions = self._get_actions(self._this_actions)
247
 
        builder.build_snapshot('this', ['base'], this_actions())
 
340
        actions_this = self._get_actions(self._this['actions'])()
 
341
        builder.build_snapshot('this', ['base'], actions_this)
 
342
        # builder.get_branch() tip is now 'this'
 
343
 
248
344
        builder.finish_series()
249
345
        self.builder = builder
250
346
 
254
350
    def _get_check(self, name):
255
351
        return getattr(self, 'check_%s' % name)
256
352
 
257
 
    def do_modify_file(self):
258
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
259
 
 
260
 
    def check_file_has_more_content(self):
261
 
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
262
 
 
263
 
    def do_delete_file(self):
264
 
        return [('unversion', 'file-id')]
265
 
 
266
 
    def check_file_doesnt_exist(self):
267
 
        self.failIfExists('branch/file')
268
 
 
269
353
    def _merge_other_into_this(self):
270
354
        b = self.builder.get_branch()
271
355
        wt = b.bzrdir.sprout('branch').open_workingtree()
272
356
        wt.merge_from_branch(b, 'other')
273
357
        return wt
274
358
 
275
 
    def assertConflict(self, wt, ctype, **kwargs):
 
359
    def assertConflict(self, wt):
276
360
        confs = wt.conflicts()
277
361
        self.assertLength(1, confs)
278
362
        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)
 
363
        self.assertIsInstance(c, self._conflict_type)
 
364
        self._assert_conflict(wt, c)
 
365
 
 
366
    def _get_resolve_path_arg(self, wt, action):
 
367
        raise NotImplementedError(self._get_resolve_path_arg)
 
368
 
 
369
    def check_resolved(self, wt, action):
 
370
        path = self._get_resolve_path_arg(wt, action)
 
371
        conflicts.resolve(wt, [path], action=action)
286
372
        # Check that we don't have any conflicts nor unknown left
287
373
        self.assertLength(0, wt.conflicts())
288
374
        self.assertLength(0, list(wt.unknowns()))
289
375
 
290
376
    def test_resolve_taking_this(self):
291
377
        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')
295
 
        check_this = self._get_check(self._check_this)
 
378
        self.assertConflict(wt)
 
379
        self.check_resolved(wt, 'take_this')
 
380
        check_this = self._get_check(self._this['check'])
296
381
        check_this()
297
382
 
298
383
    def test_resolve_taking_other(self):
299
384
        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')
303
 
        check_other = self._get_check(self._check_other)
 
385
        self.assertConflict(wt)
 
386
        self.check_resolved(wt, 'take_other')
 
387
        check_other = self._get_check(self._other['check'])
304
388
        check_other()
305
389
 
306
390
 
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
 
""")
 
391
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
 
392
 
 
393
    _conflict_type = conflicts.ContentsConflict,
 
394
 
 
395
    # Set by load_tests from scenarios()
 
396
    # path and file-id for the file involved in the conflict
 
397
    _path = None
 
398
    _file_id = None
 
399
 
 
400
    @staticmethod
 
401
    def scenarios():
 
402
        base_scenarios = [
 
403
            # File modified/deleted
 
404
            (dict(_base_actions='create_file',
 
405
                  _path='file', _file_id='file-id'),
 
406
             ('file_modified',
 
407
              dict(actions='modify_file', check='file_has_more_content')),
 
408
             ('file_deleted',
 
409
              dict(actions='delete_file', check='file_doesnt_exist')),),
 
410
            ]
 
411
        return mirror_scenarios(base_scenarios)
 
412
 
 
413
    def do_create_file(self):
 
414
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
 
415
 
 
416
    def do_modify_file(self):
 
417
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
418
 
 
419
    def check_file_has_more_content(self):
 
420
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
 
421
 
 
422
    def do_delete_file(self):
 
423
        return [('unversion', 'file-id')]
 
424
 
 
425
    def check_file_doesnt_exist(self):
 
426
        self.failIfExists('branch/file')
 
427
 
 
428
    def _get_resolve_path_arg(self, wt, action):
 
429
        return self._path
 
430
 
 
431
    def assertContentsConflict(self, wt, c):
 
432
        self.assertEqual(self._file_id, c.file_id)
 
433
        self.assertEqual(self._path, c.path)
 
434
    _assert_conflict = assertContentsConflict
 
435
 
 
436
 
 
437
class TestResolvePathConflict(TestParametrizedResolveConflicts):
 
438
 
 
439
    _conflict_type = conflicts.PathConflict,
 
440
 
 
441
    def do_nothing(self):
 
442
        return []
 
443
 
 
444
    @staticmethod
 
445
    def scenarios():
 
446
        # Each side dict additionally defines:
 
447
        # - path path involved (can be '<deleted>')
 
448
        # - file-id involved
 
449
        base_scenarios = [
 
450
            # File renamed/deleted
 
451
            (dict(_base_actions='create_file'),
 
452
             ('file_renamed',
 
453
              dict(actions='rename_file', check='file_renamed',
 
454
                   path='new-file', file_id='file-id')),
 
455
             ('file_deleted',
 
456
              dict(actions='delete_file', check='file_doesnt_exist',
 
457
                   # PathConflicts deletion handling requires a special
 
458
                   # hard-coded value
 
459
                   path='<deleted>', file_id='file-id')),),
 
460
            # File renamed/renamed differently
 
461
            (dict(_base_actions='create_file'),
 
462
             ('file_renamed',
 
463
              dict(actions='rename_file', check='file_renamed',
 
464
                   path='new-file', file_id='file-id')),
 
465
             ('file_renamed2',
 
466
              dict(actions='rename_file2', check='file_renamed2',
 
467
                   path='new-file2', file_id='file-id')),),
 
468
            # Dir renamed/deleted
 
469
            (dict(_base_actions='create_dir'),
 
470
             ('dir_renamed',
 
471
              dict(actions='rename_dir', check='dir_renamed',
 
472
                   path='new-dir', file_id='dir-id')),
 
473
             ('dir_deleted',
 
474
              dict(actions='delete_dir', check='dir_doesnt_exist',
 
475
                   # PathConflicts deletion handling requires a special
 
476
                   # hard-coded value
 
477
                   path='<deleted>', file_id='dir-id')),),
 
478
            # Dir renamed/renamed differently
 
479
            (dict(_base_actions='create_dir'),
 
480
             ('dir_renamed',
 
481
              dict(actions='rename_dir', check='dir_renamed',
 
482
                   path='new-dir', file_id='dir-id')),
 
483
             ('dir_renamed2',
 
484
              dict(actions='rename_dir2', check='dir_renamed2',
 
485
                   path='new-dir2', file_id='dir-id')),),
 
486
        ]
 
487
        return mirror_scenarios(base_scenarios)
 
488
 
 
489
    def do_create_file(self):
 
490
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
 
491
 
 
492
    def do_create_dir(self):
 
493
        return [('add', ('dir', 'dir-id', 'directory', ''))]
 
494
 
 
495
    def do_rename_file(self):
 
496
        return [('rename', ('file', 'new-file'))]
 
497
 
 
498
    def check_file_renamed(self):
 
499
        self.failIfExists('branch/file')
 
500
        self.failUnlessExists('branch/new-file')
 
501
 
 
502
    def do_rename_file2(self):
 
503
        return [('rename', ('file', 'new-file2'))]
 
504
 
 
505
    def check_file_renamed2(self):
 
506
        self.failIfExists('branch/file')
 
507
        self.failUnlessExists('branch/new-file2')
 
508
 
 
509
    def do_rename_dir(self):
 
510
        return [('rename', ('dir', 'new-dir'))]
 
511
 
 
512
    def check_dir_renamed(self):
 
513
        self.failIfExists('branch/dir')
 
514
        self.failUnlessExists('branch/new-dir')
 
515
 
 
516
    def do_rename_dir2(self):
 
517
        return [('rename', ('dir', 'new-dir2'))]
 
518
 
 
519
    def check_dir_renamed2(self):
 
520
        self.failIfExists('branch/dir')
 
521
        self.failUnlessExists('branch/new-dir2')
 
522
 
 
523
    def do_delete_file(self):
 
524
        return [('unversion', 'file-id')]
 
525
 
 
526
    def check_file_doesnt_exist(self):
 
527
        self.failIfExists('branch/file')
 
528
 
 
529
    def do_delete_dir(self):
 
530
        return [('unversion', 'dir-id')]
 
531
 
 
532
    def check_dir_doesnt_exist(self):
 
533
        self.failIfExists('branch/dir')
 
534
 
 
535
    def _get_resolve_path_arg(self, wt, action):
 
536
        tpath = self._this['path']
 
537
        opath = self._other['path']
 
538
        if tpath == '<deleted>':
 
539
            path = opath
 
540
        else:
 
541
            path = tpath
 
542
        return path
 
543
 
 
544
    def assertPathConflict(self, wt, c):
 
545
        tpath = self._this['path']
 
546
        tfile_id = self._this['file_id']
 
547
        opath = self._other['path']
 
548
        ofile_id = self._other['file_id']
 
549
        self.assertEqual(tfile_id, ofile_id) # Sanity check
 
550
        self.assertEqual(tfile_id, c.file_id)
 
551
        self.assertEqual(tpath, c.path)
 
552
        self.assertEqual(opath, c.conflict_path)
 
553
    _assert_conflict = assertPathConflict
 
554
 
 
555
 
 
556
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
 
557
    """Same as TestResolvePathConflict but a specific conflict object.
 
558
    """
 
559
 
 
560
    def assertPathConflict(self, c):
 
561
        # We create a conflict object as it was created before the fix and
 
562
        # inject it into the working tree, the test will exercise the
 
563
        # compatibility code.
 
564
        old_c = conflicts.PathConflict('<deleted>', self._item_path,
 
565
                                       file_id=None)
 
566
        wt.set_conflicts(conflicts.ConflictList([old_c]))
 
567
 
 
568
 
 
569
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
 
570
 
 
571
    _conflict_type = conflicts.DuplicateEntry,
 
572
 
 
573
    @staticmethod
 
574
    def scenarios():
 
575
        # Each side dict additionally defines:
 
576
        # - path involved
 
577
        # - file-id involved
 
578
        base_scenarios = [
 
579
            # File created with different file-ids
 
580
            (dict(_base_actions='nothing'),
 
581
             ('filea_created',
 
582
              dict(actions='create_file_a', check='file_content_a',
 
583
                   path='file', file_id='file-a-id')),
 
584
             ('fileb_created',
 
585
              dict(actions='create_file_b', check='file_content_b',
 
586
                   path='file', file_id='file-b-id')),),
 
587
            ]
 
588
        return mirror_scenarios(base_scenarios)
 
589
 
 
590
    def do_nothing(self):
 
591
        return []
 
592
 
 
593
    def do_create_file_a(self):
 
594
        return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
 
595
 
 
596
    def check_file_content_a(self):
 
597
        self.assertFileEqual('file a content\n', 'branch/file')
 
598
 
 
599
    def do_create_file_b(self):
 
600
        return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
 
601
 
 
602
    def check_file_content_b(self):
 
603
        self.assertFileEqual('file b content\n', 'branch/file')
 
604
 
 
605
    def _get_resolve_path_arg(self, wt, action):
 
606
        return self._this['path']
 
607
 
 
608
    def assertDuplicateEntry(self, wt, c):
 
609
        tpath = self._this['path']
 
610
        tfile_id = self._this['file_id']
 
611
        opath = self._other['path']
 
612
        ofile_id = self._other['file_id']
 
613
        self.assertEqual(tpath, opath) # Sanity check
 
614
        self.assertEqual(tfile_id, c.file_id)
 
615
        self.assertEqual(tpath + '.moved', c.path)
 
616
        self.assertEqual(tpath, c.conflict_path)
 
617
    _assert_conflict = assertDuplicateEntry
361
618
 
362
619
 
363
620
class TestResolveUnversionedParent(TestResolveConflicts):
527
784
""")
528
785
 
529
786
 
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):
580
 
 
581
 
    preamble = """
582
 
$ bzr init trunk
583
 
$ cd trunk
584
 
$ bzr mkdir dir1
585
 
$ bzr mkdir dir2
586
 
$ bzr commit -m 'Create trunk'
587
 
 
588
 
$ bzr mv dir2 dir1
589
 
$ bzr commit -m 'Moved dir2 into dir1'
590
 
 
591
 
$ bzr branch . -r 1 ../branch
592
 
$ cd ../branch
593
 
$ bzr mv dir1 dir2
594
 
$ bzr commit -m 'Moved dir1 into dir2'
595
 
 
596
 
$ bzr merge ../trunk
597
 
2>Conflict moving dir2/dir1 into dir2.  Cancelled move.
598
 
2>1 conflicts encountered.
599
 
"""
600
 
 
601
 
    def test_take_this(self):
602
 
        self.run_script("""
603
 
$ bzr resolve dir2
604
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
605
 
""")
606
 
 
607
 
    def test_take_other(self):
608
 
        self.run_script("""
609
 
$ bzr mv dir2/dir1 dir1
610
 
$ bzr mv dir2 dir1
611
 
$ bzr resolve dir2
612
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
613
 
""")
614
 
 
615
 
    def test_resolve_taking_this(self):
616
 
        self.run_script("""
617
 
$ bzr resolve --take-this dir2
618
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
619
 
""")
620
 
        self.failUnlessExists('dir2')
621
 
 
622
 
    def test_resolve_taking_other(self):
623
 
        self.run_script("""
624
 
$ bzr resolve --take-other dir2
625
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
626
 
""")
627
 
        self.failUnlessExists('dir1')
 
787
class TestResolveParentLoop(TestParametrizedResolveConflicts):
 
788
 
 
789
    _conflict_type = conflicts.ParentLoop,
 
790
 
 
791
    _this_args = None
 
792
    _other_args = None
 
793
 
 
794
    @staticmethod
 
795
    def scenarios():
 
796
        # Each side dict additionally defines:
 
797
        # - dir_id: the directory being moved
 
798
        # - target_id: The target directory
 
799
        # - xfail: whether the test is expected to fail if the action is
 
800
        #     involved as 'other'
 
801
        base_scenarios = [
 
802
            # Dirs moved into each other
 
803
            (dict(_base_actions='create_dir1_dir2'),
 
804
             ('dir1_into_dir2',
 
805
              dict(actions='move_dir1_into_dir2', check='dir1_moved',
 
806
                   dir_id='dir1-id', target_id='dir2-id', xfail=False)),
 
807
             ('dir2_into_dir1',
 
808
              dict(actions='move_dir2_into_dir1', check='dir2_moved',
 
809
                   dir_id='dir2-id', target_id='dir1-id', xfail=False))),
 
810
            # Subdirs moved into each other
 
811
            (dict(_base_actions='create_dir1_4'),
 
812
             ('dir1_into_dir4',
 
813
              dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
 
814
                   dir_id='dir1-id', target_id='dir4-id', xfail=True)),
 
815
             ('dir3_into_dir2',
 
816
              dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
 
817
                   dir_id='dir3-id', target_id='dir2-id', xfail=True))),
 
818
            ]
 
819
        return mirror_scenarios(base_scenarios)
 
820
 
 
821
    def do_create_dir1_dir2(self):
 
822
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
 
823
                ('add', ('dir2', 'dir2-id', 'directory', '')),]
 
824
 
 
825
    def do_move_dir1_into_dir2(self):
 
826
        return [('rename', ('dir1', 'dir2/dir1'))]
 
827
 
 
828
    def check_dir1_moved(self):
 
829
        self.failIfExists('branch/dir1')
 
830
        self.failUnlessExists('branch/dir2/dir1')
 
831
 
 
832
    def do_move_dir2_into_dir1(self):
 
833
        return [('rename', ('dir2', 'dir1/dir2'))]
 
834
 
 
835
    def check_dir2_moved(self):
 
836
        self.failIfExists('branch/dir2')
 
837
        self.failUnlessExists('branch/dir1/dir2')
 
838
 
 
839
    def do_create_dir1_4(self):
 
840
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
 
841
                ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
 
842
                ('add', ('dir3', 'dir3-id', 'directory', '')),
 
843
                ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
 
844
 
 
845
    def do_move_dir1_into_dir4(self):
 
846
        return [('rename', ('dir1', 'dir3/dir4/dir1'))]
 
847
 
 
848
    def check_dir1_2_moved(self):
 
849
        self.failIfExists('branch/dir1')
 
850
        self.failUnlessExists('branch/dir3/dir4/dir1')
 
851
        self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
 
852
 
 
853
    def do_move_dir3_into_dir2(self):
 
854
        return [('rename', ('dir3', 'dir1/dir2/dir3'))]
 
855
 
 
856
    def check_dir3_4_moved(self):
 
857
        self.failIfExists('branch/dir3')
 
858
        self.failUnlessExists('branch/dir1/dir2/dir3')
 
859
        self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
 
860
 
 
861
    def _get_resolve_path_arg(self, wt, action):
 
862
        # ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
 
863
        # But since <path> doesn't exist in the working tree, we need to use
 
864
        # <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
 
865
        return wt.id2path(self._other['dir_id'])
 
866
 
 
867
    def assertParentLoop(self, wt, c):
 
868
        self.assertEqual(self._other['dir_id'], c.file_id)
 
869
        self.assertEqual(self._other['target_id'], c.conflict_file_id)
 
870
        # The conflict paths are irrelevant (they are deterministic but not
 
871
        # worth checking since they don't provide the needed information
 
872
        # anyway)
 
873
        if self._other['xfail']:
 
874
            # It's a bit hackish to raise from here relying on being called for
 
875
            # both tests but this avoid overriding test_resolve_taking_other
 
876
            raise tests.KnownFailure(
 
877
                "ParentLoop doesn't carry enough info to resolve --take-other")
 
878
    _assert_conflict = assertParentLoop
628
879
 
629
880
 
630
881
class TestResolveNonDirectoryParent(TestResolveConflicts):