~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Martin
  • Date: 2011-04-15 21:22:57 UTC
  • mto: This revision was merged to the branch mainline in revision 5797.
  • Revision ID: gzlist@googlemail.com-20110415212257-jgtovwwp4be7egd9
Add release notes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
import os
19
19
 
20
20
from bzrlib import (
21
 
    branchbuilder,
22
21
    bzrdir,
23
22
    conflicts,
24
23
    errors,
25
24
    option,
 
25
    osutils,
26
26
    tests,
27
 
    workingtree,
28
 
    )
29
 
from bzrlib.tests import script
30
 
 
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
 
27
    )
 
28
from bzrlib.tests import (
 
29
    script,
 
30
    scenarios,
 
31
    )
 
32
 
 
33
 
 
34
load_tests = scenarios.load_tests_apply_scenarios
49
35
 
50
36
 
51
37
# TODO: Test commit with some added, and added-but-missing files
78
64
 
79
65
class TestConflicts(tests.TestCaseWithTransport):
80
66
 
81
 
    def test_conflicts(self):
82
 
        """Conflicts are detected properly"""
83
 
        # Use BzrDirFormat6 so we can fake conflicts
84
 
        tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
85
 
        self.build_tree_contents([('hello', 'hello world4'),
86
 
                                  ('hello.THIS', 'hello world2'),
87
 
                                  ('hello.BASE', 'hello world1'),
88
 
                                  ('hello.OTHER', 'hello world3'),
89
 
                                  ('hello.sploo.BASE', 'yellowworld'),
90
 
                                  ('hello.sploo.OTHER', 'yellowworld2'),
91
 
                                  ])
92
 
        tree.lock_read()
93
 
        self.assertLength(6, list(tree.list_files()))
94
 
        tree.unlock()
95
 
        tree_conflicts = tree.conflicts()
96
 
        self.assertLength(2, tree_conflicts)
97
 
        self.assertTrue('hello' in tree_conflicts[0].path)
98
 
        self.assertTrue('hello.sploo' in tree_conflicts[1].path)
99
 
        conflicts.restore('hello')
100
 
        conflicts.restore('hello.sploo')
101
 
        self.assertLength(0, tree.conflicts())
102
 
        self.assertFileEqual('hello world2', 'hello')
103
 
        self.assertFalse(os.path.lexists('hello.sploo'))
104
 
        self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
105
 
        self.assertRaises(errors.NotConflicted,
106
 
                          conflicts.restore, 'hello.sploo')
107
 
 
108
67
    def test_resolve_conflict_dir(self):
109
68
        tree = self.make_branch_and_tree('.')
110
69
        self.build_tree_contents([('hello', 'hello world4'),
210
169
        self.run_script(self.preamble)
211
170
 
212
171
 
213
 
class TestResolveTextConflicts(TestResolveConflicts):
214
 
    # TBC
215
 
    pass
216
 
 
217
 
 
218
172
def mirror_scenarios(base_scenarios):
219
173
    """Return a list of mirrored scenarios.
220
174
 
293
247
    _this = None
294
248
    _other = None
295
249
 
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 []
 
250
    scenarios = []
 
251
    """The scenario list for the conflict type defined by the class.
 
252
 
 
253
    Each scenario is of the form:
 
254
    (common, (left_name, left_dict), (right_name, right_dict))
 
255
 
 
256
    * common is a dict
 
257
 
 
258
    * left_name and right_name are the scenario names that will be combined
 
259
 
 
260
    * left_dict and right_dict are the attributes specific to each half of
 
261
      the scenario. They should include at least 'actions' and 'check' and
 
262
      will be available as '_this' and '_other' test instance attributes.
 
263
 
 
264
    Daughters classes are free to add their specific attributes as they see
 
265
    fit in any of the three dicts.
 
266
 
 
267
    This is a class method so that load_tests can find it.
 
268
 
 
269
    '_base_actions' in the common dict, 'actions' and 'check' in the left
 
270
    and right dicts use names that map to methods in the test classes. Some
 
271
    prefixes are added to these names to get the correspong methods (see
 
272
    _get_actions() and _get_check()). The motivation here is to avoid
 
273
    collisions in the class namespace.
 
274
    """
324
275
 
325
276
    def setUp(self):
326
277
        super(TestParametrizedResolveConflicts, self).setUp()
388
339
        check_other()
389
340
 
390
341
 
 
342
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
 
343
 
 
344
    _conflict_type = conflicts.TextConflict
 
345
 
 
346
    # Set by the scenarios
 
347
    # path and file-id for the file involved in the conflict
 
348
    _path = None
 
349
    _file_id = None
 
350
 
 
351
    scenarios = mirror_scenarios(
 
352
        [
 
353
            # File modified on both sides
 
354
            (dict(_base_actions='create_file',
 
355
                  _path='file', _file_id='file-id'),
 
356
             ('filed_modified_A',
 
357
              dict(actions='modify_file_A', check='file_has_content_A')),
 
358
             ('file_modified_B',
 
359
              dict(actions='modify_file_B', check='file_has_content_B')),),
 
360
            # File modified on both sides in dir
 
361
            (dict(_base_actions='create_file_in_dir',
 
362
                  _path='dir/file', _file_id='file-id'),
 
363
             ('filed_modified_A_in_dir',
 
364
              dict(actions='modify_file_A',
 
365
                   check='file_in_dir_has_content_A')),
 
366
             ('file_modified_B',
 
367
              dict(actions='modify_file_B',
 
368
                   check='file_in_dir_has_content_B')),),
 
369
            ])
 
370
 
 
371
    def do_create_file(self, path='file'):
 
372
        return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
 
373
 
 
374
    def do_modify_file_A(self):
 
375
        return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
 
376
 
 
377
    def do_modify_file_B(self):
 
378
        return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
 
379
 
 
380
    def check_file_has_content_A(self, path='file'):
 
381
        self.assertFileEqual('trunk content\nfeature A\n',
 
382
                             osutils.pathjoin('branch', path))
 
383
 
 
384
    def check_file_has_content_B(self, path='file'):
 
385
        self.assertFileEqual('trunk content\nfeature B\n',
 
386
                             osutils.pathjoin('branch', path))
 
387
 
 
388
    def do_create_file_in_dir(self):
 
389
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
390
            ] + self.do_create_file('dir/file')
 
391
 
 
392
    def check_file_in_dir_has_content_A(self):
 
393
        self.check_file_has_content_A('dir/file')
 
394
 
 
395
    def check_file_in_dir_has_content_B(self):
 
396
        self.check_file_has_content_B('dir/file')
 
397
 
 
398
    def _get_resolve_path_arg(self, wt, action):
 
399
        return self._path
 
400
 
 
401
    def assertTextConflict(self, wt, c):
 
402
        self.assertEqual(self._file_id, c.file_id)
 
403
        self.assertEqual(self._path, c.path)
 
404
    _assert_conflict = assertTextConflict
 
405
 
 
406
 
391
407
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
392
408
 
393
 
    _conflict_type = conflicts.ContentsConflict,
 
409
    _conflict_type = conflicts.ContentsConflict
394
410
 
395
 
    # Set by load_tests from scenarios()
 
411
    # Set by the scenarios
396
412
    # path and file-id for the file involved in the conflict
397
413
    _path = None
398
414
    _file_id = None
399
415
 
400
 
    @staticmethod
401
 
    def scenarios():
402
 
        base_scenarios = [
 
416
    scenarios = mirror_scenarios(
 
417
        [
403
418
            # File modified/deleted
404
419
            (dict(_base_actions='create_file',
405
420
                  _path='file', _file_id='file-id'),
407
422
              dict(actions='modify_file', check='file_has_more_content')),
408
423
             ('file_deleted',
409
424
              dict(actions='delete_file', check='file_doesnt_exist')),),
410
 
            ]
411
 
        return mirror_scenarios(base_scenarios)
 
425
            # File modified/deleted in dir
 
426
            (dict(_base_actions='create_file_in_dir',
 
427
                  _path='dir/file', _file_id='file-id'),
 
428
             ('file_modified_in_dir',
 
429
              dict(actions='modify_file_in_dir',
 
430
                   check='file_in_dir_has_more_content')),
 
431
             ('file_deleted_in_dir',
 
432
              dict(actions='delete_file',
 
433
                   check='file_in_dir_doesnt_exist')),),
 
434
            ])
412
435
 
413
436
    def do_create_file(self):
414
437
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
425
448
    def check_file_doesnt_exist(self):
426
449
        self.failIfExists('branch/file')
427
450
 
 
451
    def do_create_file_in_dir(self):
 
452
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
453
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
 
454
 
 
455
    def do_modify_file_in_dir(self):
 
456
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
457
 
 
458
    def check_file_in_dir_has_more_content(self):
 
459
        self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
 
460
 
 
461
    def check_file_in_dir_doesnt_exist(self):
 
462
        self.failIfExists('branch/dir/file')
 
463
 
428
464
    def _get_resolve_path_arg(self, wt, action):
429
465
        return self._path
430
466
 
436
472
 
437
473
class TestResolvePathConflict(TestParametrizedResolveConflicts):
438
474
 
439
 
    _conflict_type = conflicts.PathConflict,
 
475
    _conflict_type = conflicts.PathConflict
440
476
 
441
477
    def do_nothing(self):
442
478
        return []
443
479
 
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 = [
 
480
    # Each side dict additionally defines:
 
481
    # - path path involved (can be '<deleted>')
 
482
    # - file-id involved
 
483
    scenarios = mirror_scenarios(
 
484
        [
450
485
            # File renamed/deleted
451
486
            (dict(_base_actions='create_file'),
452
487
             ('file_renamed',
457
492
                   # PathConflicts deletion handling requires a special
458
493
                   # hard-coded value
459
494
                   path='<deleted>', file_id='file-id')),),
 
495
            # File renamed/deleted in dir
 
496
            (dict(_base_actions='create_file_in_dir'),
 
497
             ('file_renamed_in_dir',
 
498
              dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
 
499
                   path='dir/new-file', file_id='file-id')),
 
500
             ('file_deleted',
 
501
              dict(actions='delete_file', check='file_in_dir_doesnt_exist',
 
502
                   # PathConflicts deletion handling requires a special
 
503
                   # hard-coded value
 
504
                   path='<deleted>', file_id='file-id')),),
460
505
            # File renamed/renamed differently
461
506
            (dict(_base_actions='create_file'),
462
507
             ('file_renamed',
483
528
             ('dir_renamed2',
484
529
              dict(actions='rename_dir2', check='dir_renamed2',
485
530
                   path='new-dir2', file_id='dir-id')),),
486
 
        ]
487
 
        return mirror_scenarios(base_scenarios)
 
531
            ])
488
532
 
489
533
    def do_create_file(self):
490
534
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
532
576
    def check_dir_doesnt_exist(self):
533
577
        self.failIfExists('branch/dir')
534
578
 
 
579
    def do_create_file_in_dir(self):
 
580
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
581
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
 
582
 
 
583
    def do_rename_file_in_dir(self):
 
584
        return [('rename', ('dir/file', 'dir/new-file'))]
 
585
 
 
586
    def check_file_in_dir_renamed(self):
 
587
        self.failIfExists('branch/dir/file')
 
588
        self.failUnlessExists('branch/dir/new-file')
 
589
 
 
590
    def check_file_in_dir_doesnt_exist(self):
 
591
        self.failIfExists('branch/dir/file')
 
592
 
535
593
    def _get_resolve_path_arg(self, wt, action):
536
594
        tpath = self._this['path']
537
595
        opath = self._other['path']
568
626
 
569
627
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
570
628
 
571
 
    _conflict_type = conflicts.DuplicateEntry,
 
629
    _conflict_type = conflicts.DuplicateEntry
572
630
 
573
 
    @staticmethod
574
 
    def scenarios():
575
 
        # Each side dict additionally defines:
576
 
        # - path involved
577
 
        # - file-id involved
578
 
        base_scenarios = [
 
631
    scenarios = mirror_scenarios(
 
632
        [
579
633
            # File created with different file-ids
580
634
            (dict(_base_actions='nothing'),
581
635
             ('filea_created',
584
638
             ('fileb_created',
585
639
              dict(actions='create_file_b', check='file_content_b',
586
640
                   path='file', file_id='file-b-id')),),
587
 
            ]
588
 
        return mirror_scenarios(base_scenarios)
 
641
            ])
589
642
 
590
643
    def do_nothing(self):
591
644
        return []
649
702
        self.run_script("""
650
703
$ bzr rm -q dir  --force
651
704
$ bzr resolve dir
 
705
2>2 conflict(s) resolved, 0 remaining
652
706
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
653
707
""")
654
708
 
655
709
    def test_take_other(self):
656
710
        self.run_script("""
657
711
$ bzr resolve dir
 
712
2>2 conflict(s) resolved, 0 remaining
658
713
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
659
714
""")
660
715
 
688
743
    def test_keep_them_all(self):
689
744
        self.run_script("""
690
745
$ bzr resolve dir
 
746
2>2 conflict(s) resolved, 0 remaining
691
747
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
692
748
""")
693
749
 
696
752
$ bzr mv -q dir/file2 file2
697
753
$ bzr rm -q dir --force
698
754
$ bzr resolve dir
 
755
2>2 conflict(s) resolved, 0 remaining
699
756
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
700
757
""")
701
758
 
703
760
        self.run_script("""
704
761
$ bzr rm -q dir --force
705
762
$ bzr resolve dir
 
763
2>2 conflict(s) resolved, 0 remaining
706
764
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
707
765
""")
708
766
 
749
807
    def test_keep_them_all(self):
750
808
        self.run_script("""
751
809
$ bzr resolve dir
 
810
2>2 conflict(s) resolved, 0 remaining
752
811
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
753
812
""")
754
813
 
757
816
$ bzr mv -q dir/file2 file2
758
817
$ bzr rm -q dir --force
759
818
$ bzr resolve dir
 
819
2>2 conflict(s) resolved, 0 remaining
760
820
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
761
821
""")
762
822
 
764
824
        self.run_script("""
765
825
$ bzr rm -q dir --force
766
826
$ bzr resolve dir
 
827
2>2 conflict(s) resolved, 0 remaining
767
828
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
768
829
""")
769
830
 
770
831
    def test_resolve_taking_this(self):
771
832
        self.run_script("""
772
833
$ bzr resolve --take-this dir
 
834
2>2 conflict(s) resolved, 0 remaining
773
835
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
774
836
""")
775
837
 
778
840
$ bzr resolve --take-other dir
779
841
2>deleted dir/file2
780
842
2>deleted dir
 
843
2>2 conflict(s) resolved, 0 remaining
781
844
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
782
845
""")
783
846
 
784
847
 
785
848
class TestResolveParentLoop(TestParametrizedResolveConflicts):
786
849
 
787
 
    _conflict_type = conflicts.ParentLoop,
 
850
    _conflict_type = conflicts.ParentLoop
788
851
 
789
852
    _this_args = None
790
853
    _other_args = None
791
854
 
792
 
    @staticmethod
793
 
    def scenarios():
794
 
        # Each side dict additionally defines:
795
 
        # - dir_id: the directory being moved
796
 
        # - target_id: The target directory
797
 
        # - xfail: whether the test is expected to fail if the action is
798
 
        #     involved as 'other'
799
 
        base_scenarios = [
 
855
    # Each side dict additionally defines:
 
856
    # - dir_id: the directory being moved
 
857
    # - target_id: The target directory
 
858
    # - xfail: whether the test is expected to fail if the action is
 
859
    #   involved as 'other'
 
860
    scenarios = mirror_scenarios(
 
861
        [
800
862
            # Dirs moved into each other
801
863
            (dict(_base_actions='create_dir1_dir2'),
802
864
             ('dir1_into_dir2',
813
875
             ('dir3_into_dir2',
814
876
              dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
815
877
                   dir_id='dir3-id', target_id='dir2-id', xfail=True))),
816
 
            ]
817
 
        return mirror_scenarios(base_scenarios)
 
878
            ])
818
879
 
819
880
    def do_create_dir1_dir2(self):
820
881
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
909
970
# aside ? -- vila 090916
910
971
$ bzr add -q foo
911
972
$ bzr resolve foo.new
 
973
2>1 conflict(s) resolved, 0 remaining
912
974
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
913
975
""")
914
976
 
917
979
$ bzr rm -q foo --force
918
980
$ bzr mv -q foo.new foo
919
981
$ bzr resolve foo
 
982
2>1 conflict(s) resolved, 0 remaining
920
983
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
921
984
""")
922
985