~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge.py

  • Committer: Vincent Ladeuil
  • Date: 2010-01-25 15:55:48 UTC
  • mto: (4985.1.4 add-attr-cleanup)
  • mto: This revision was merged to the branch mainline in revision 4988.
  • Revision ID: v.ladeuil+lp@free.fr-20100125155548-0l352pujvt5bzl5e
Deploy addAttrCleanup on the whole test suite.

Several use case worth mentioning:

- setting a module or any other object attribute is the majority
by far. In some cases the setting itself is deferred but most of
the time we want to set at the same time we add the cleanup.

- there multiple occurrences of protecting hooks or ui factory
which are now useless (the test framework takes care of that now),

- there was some lambda uses that can now be avoided.

That first cleanup already simplifies things a lot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
    transform,
30
30
    versionedfile,
31
31
    )
32
 
from bzrlib.branch import Branch
33
32
from bzrlib.conflicts import ConflictList, TextConflict
34
 
from bzrlib.errors import UnrelatedBranches, NoCommits, BzrCommandError
 
33
from bzrlib.errors import UnrelatedBranches, NoCommits
35
34
from bzrlib.merge import transform_tree, merge_inner, _PlanMerge
36
35
from bzrlib.osutils import pathjoin, file_kind
37
36
from bzrlib.tests import TestCaseWithTransport, TestCaseWithMemoryTransport
151
150
        log = StringIO()
152
151
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
153
152
                    this_tree=tree_b, ignore_zero=True)
154
 
        log = self._get_log(keep_log_file=True)
155
 
        self.failUnless('All changes applied successfully.\n' not in log)
 
153
        self.failUnless('All changes applied successfully.\n' not in
 
154
            self.get_log())
156
155
        tree_b.revert()
157
156
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
158
157
                    this_tree=tree_b, ignore_zero=False)
159
 
        log = self._get_log(keep_log_file=True)
160
 
        self.failUnless('All changes applied successfully.\n' in log)
 
158
        self.failUnless('All changes applied successfully.\n' in self.get_log())
161
159
 
162
160
    def test_merge_inner_conflicts(self):
163
161
        tree_a = self.make_branch_and_tree('a')
218
216
        tree_a.add('file')
219
217
        tree_a.commit('commit base')
220
218
        # basis_tree() is only guaranteed to be valid as long as it is actually
221
 
        # the basis tree. This mutates the tree after grabbing basis, so go to
222
 
        # the repository.
 
219
        # the basis tree. This test commits to the tree after grabbing basis,
 
220
        # so we go to the repository.
223
221
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
224
222
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
225
223
        self.build_tree_contents([('tree_a/file', 'content_2')])
226
224
        tree_a.commit('commit other')
227
225
        other_tree = tree_a.basis_tree()
 
226
        # 'file' is now missing but isn't altered in any commit in b so no
 
227
        # change should be applied.
228
228
        os.unlink('tree_b/file')
229
229
        merge_inner(tree_b.branch, other_tree, base_tree, this_tree=tree_b)
230
230
 
522
522
        self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
523
523
        return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
524
524
 
 
525
    def test_base_from_plan(self):
 
526
        self.setup_plan_merge()
 
527
        plan = self.plan_merge_vf.plan_merge('B', 'C')
 
528
        pwm = versionedfile.PlanWeaveMerge(plan)
 
529
        self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
 
530
 
525
531
    def test_unique_lines(self):
526
532
        plan = self.setup_plan_merge()
527
533
        self.assertEqual(plan._unique_lines(
825
831
                          ('unchanged', 'f\n'),
826
832
                          ('unchanged', 'g\n')],
827
833
                         list(plan))
 
834
        plan = self.plan_merge_vf.plan_lca_merge('F', 'G')
 
835
        # This is one of the main differences between plan_merge and
 
836
        # plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
 
837
        # because 'x' was not present in one of the bases. However, in this
 
838
        # case it is spurious because 'x' does not exist in the global base A.
 
839
        self.assertEqual([
 
840
                          ('unchanged', 'h\n'),
 
841
                          ('unchanged', 'a\n'),
 
842
                          ('conflicted-a', 'x\n'),
 
843
                          ('new-b', 'z\n'),
 
844
                          ('unchanged', 'c\n'),
 
845
                          ('unchanged', 'd\n'),
 
846
                          ('unchanged', 'y\n'),
 
847
                          ('unchanged', 'f\n'),
 
848
                          ('unchanged', 'g\n')],
 
849
                         list(plan))
 
850
 
 
851
    def test_criss_cross_flip_flop(self):
 
852
        # This is specificly trying to trigger problems when using limited
 
853
        # ancestry and weaves. The ancestry graph looks like:
 
854
        #       XX      unused ancestor, should not show up in the weave
 
855
        #       |
 
856
        #       A       Unique LCA
 
857
        #      / \  
 
858
        #     B   C     B & C both introduce a new line
 
859
        #     |\ /|  
 
860
        #     | X |  
 
861
        #     |/ \| 
 
862
        #     D   E     B & C are both merged, so both are common ancestors
 
863
        #               In the process of merging, both sides order the new
 
864
        #               lines differently
 
865
        #
 
866
        self.add_rev('root', 'XX', [], 'qrs')
 
867
        self.add_rev('root', 'A', ['XX'], 'abcdef')
 
868
        self.add_rev('root', 'B', ['A'], 'abcdgef')
 
869
        self.add_rev('root', 'C', ['A'], 'abcdhef')
 
870
        self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
 
871
        self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
 
872
        plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
 
873
        self.assertEqual([
 
874
                          ('unchanged', 'a\n'),
 
875
                          ('unchanged', 'b\n'),
 
876
                          ('unchanged', 'c\n'),
 
877
                          ('unchanged', 'd\n'),
 
878
                          ('new-b', 'h\n'),
 
879
                          ('unchanged', 'g\n'),
 
880
                          ('killed-b', 'h\n'),
 
881
                          ('unchanged', 'e\n'),
 
882
                          ('unchanged', 'f\n'),
 
883
                         ], plan)
 
884
        pwm = versionedfile.PlanWeaveMerge(plan)
 
885
        self.assertEqualDiff('\n'.join('abcdghef') + '\n',
 
886
                             ''.join(pwm.base_from_plan()))
 
887
        # Reversing the order reverses the merge plan, and final order of 'hg'
 
888
        # => 'gh'
 
889
        plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
 
890
        self.assertEqual([
 
891
                          ('unchanged', 'a\n'),
 
892
                          ('unchanged', 'b\n'),
 
893
                          ('unchanged', 'c\n'),
 
894
                          ('unchanged', 'd\n'),
 
895
                          ('new-b', 'g\n'),
 
896
                          ('unchanged', 'h\n'),
 
897
                          ('killed-b', 'g\n'),
 
898
                          ('unchanged', 'e\n'),
 
899
                          ('unchanged', 'f\n'),
 
900
                         ], plan)
 
901
        pwm = versionedfile.PlanWeaveMerge(plan)
 
902
        self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
 
903
                             ''.join(pwm.base_from_plan()))
 
904
        # This is where lca differs, in that it (fairly correctly) determines
 
905
        # that there is a conflict because both sides resolved the merge
 
906
        # differently
 
907
        plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
 
908
        self.assertEqual([
 
909
                          ('unchanged', 'a\n'),
 
910
                          ('unchanged', 'b\n'),
 
911
                          ('unchanged', 'c\n'),
 
912
                          ('unchanged', 'd\n'),
 
913
                          ('conflicted-b', 'h\n'),
 
914
                          ('unchanged', 'g\n'),
 
915
                          ('conflicted-a', 'h\n'),
 
916
                          ('unchanged', 'e\n'),
 
917
                          ('unchanged', 'f\n'),
 
918
                         ], plan)
 
919
        pwm = versionedfile.PlanWeaveMerge(plan)
 
920
        self.assertEqualDiff('\n'.join('abcdgef') + '\n',
 
921
                             ''.join(pwm.base_from_plan()))
 
922
        # Reversing it changes what line is doubled, but still gives a
 
923
        # double-conflict
 
924
        plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
 
925
        self.assertEqual([
 
926
                          ('unchanged', 'a\n'),
 
927
                          ('unchanged', 'b\n'),
 
928
                          ('unchanged', 'c\n'),
 
929
                          ('unchanged', 'd\n'),
 
930
                          ('conflicted-b', 'g\n'),
 
931
                          ('unchanged', 'h\n'),
 
932
                          ('conflicted-a', 'g\n'),
 
933
                          ('unchanged', 'e\n'),
 
934
                          ('unchanged', 'f\n'),
 
935
                         ], plan)
 
936
        pwm = versionedfile.PlanWeaveMerge(plan)
 
937
        self.assertEqualDiff('\n'.join('abcdhef') + '\n',
 
938
                             ''.join(pwm.base_from_plan()))
828
939
 
829
940
    def assertRemoveExternalReferences(self, filtered_parent_map,
830
941
                                       child_map, tails, parent_map):
1030
1141
                         ], list(plan))
1031
1142
 
1032
1143
 
1033
 
class TestMergeImplementation(object):
1034
 
 
1035
 
    def do_merge(self, target_tree, source_tree, **kwargs):
1036
 
        merger = _mod_merge.Merger.from_revision_ids(progress.DummyProgress(),
1037
 
            target_tree, source_tree.last_revision(),
1038
 
            other_branch=source_tree.branch)
1039
 
        merger.merge_type=self.merge_type
1040
 
        for name, value in kwargs.items():
1041
 
            setattr(merger, name, value)
1042
 
        merger.do_merge()
1043
 
 
1044
 
    def test_merge_specific_file(self):
1045
 
        this_tree = self.make_branch_and_tree('this')
1046
 
        this_tree.lock_write()
1047
 
        self.addCleanup(this_tree.unlock)
1048
 
        self.build_tree_contents([
1049
 
            ('this/file1', 'a\nb\n'),
1050
 
            ('this/file2', 'a\nb\n')
1051
 
        ])
1052
 
        this_tree.add(['file1', 'file2'])
1053
 
        this_tree.commit('Added files')
1054
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
1055
 
        self.build_tree_contents([
1056
 
            ('other/file1', 'a\nb\nc\n'),
1057
 
            ('other/file2', 'a\nb\nc\n')
1058
 
        ])
1059
 
        other_tree.commit('modified both')
1060
 
        self.build_tree_contents([
1061
 
            ('this/file1', 'd\na\nb\n'),
1062
 
            ('this/file2', 'd\na\nb\n')
1063
 
        ])
1064
 
        this_tree.commit('modified both')
1065
 
        self.do_merge(this_tree, other_tree, interesting_files=['file1'])
1066
 
        self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
1067
 
        self.assertFileEqual('d\na\nb\n', 'this/file2')
1068
 
 
1069
 
    def test_merge_move_and_change(self):
1070
 
        this_tree = self.make_branch_and_tree('this')
1071
 
        this_tree.lock_write()
1072
 
        self.addCleanup(this_tree.unlock)
1073
 
        self.build_tree_contents([
1074
 
            ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
1075
 
        ])
1076
 
        this_tree.add('file1',)
1077
 
        this_tree.commit('Added file')
1078
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
1079
 
        self.build_tree_contents([
1080
 
            ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
1081
 
        ])
1082
 
        other_tree.commit('Changed 2 to 2.1')
1083
 
        self.build_tree_contents([
1084
 
            ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
1085
 
        ])
1086
 
        this_tree.commit('Swapped 2 & 3')
1087
 
        self.do_merge(this_tree, other_tree)
1088
 
        self.assertFileEqual('line 1\n'
1089
 
            '<<<<<<< TREE\n'
1090
 
            'line 3\n'
1091
 
            'line 2\n'
1092
 
            '=======\n'
1093
 
            'line 2 to 2.1\n'
1094
 
            'line 3\n'
1095
 
            '>>>>>>> MERGE-SOURCE\n'
1096
 
            'line 4\n', 'this/file1')
1097
 
 
1098
 
    def test_modify_conflicts_with_delete(self):
1099
 
        # If one side deletes a line, and the other modifies that line, then
1100
 
        # the modification should be considered a conflict
1101
 
        builder = self.make_branch_builder('test')
1102
 
        builder.start_series()
1103
 
        builder.build_snapshot('BASE-id', None,
1104
 
            [('add', ('', None, 'directory', None)),
1105
 
             ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
1106
 
            ])
1107
 
        # Delete 'b\n'
1108
 
        builder.build_snapshot('OTHER-id', ['BASE-id'],
1109
 
            [('modify', ('foo-id', 'a\nc\nd\ne\n'))])
1110
 
        # Modify 'b\n', add 'X\n'
1111
 
        builder.build_snapshot('THIS-id', ['BASE-id'],
1112
 
            [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))])
1113
 
        builder.finish_series()
1114
 
        branch = builder.get_branch()
1115
 
        this_tree = branch.bzrdir.create_workingtree()
1116
 
        this_tree.lock_write()
1117
 
        self.addCleanup(this_tree.unlock)
1118
 
        other_tree = this_tree.bzrdir.sprout('other', 'OTHER-id').open_workingtree()
1119
 
        self.do_merge(this_tree, other_tree)
1120
 
        if self.merge_type is _mod_merge.LCAMerger:
1121
 
            self.expectFailure("lca merge doesn't track deleted lines",
1122
 
                self.assertFileEqual,
1123
 
                    'a\n'
1124
 
                    '<<<<<<< TREE\n'
1125
 
                    'b2\n'
1126
 
                    '=======\n'
1127
 
                    '>>>>>>> MERGE-SOURCE\n'
1128
 
                    'c\n'
1129
 
                    'd\n'
1130
 
                    'X\n'
1131
 
                    'e\n', 'test/foo')
1132
 
        else:
1133
 
            self.assertFileEqual(
1134
 
                'a\n'
1135
 
                '<<<<<<< TREE\n'
1136
 
                'b2\n'
1137
 
                '=======\n'
1138
 
                '>>>>>>> MERGE-SOURCE\n'
1139
 
                'c\n'
1140
 
                'd\n'
1141
 
                'X\n'
1142
 
                'e\n', 'test/foo')
1143
 
 
1144
 
 
1145
 
class TestMerge3Merge(TestCaseWithTransport, TestMergeImplementation):
1146
 
 
1147
 
    merge_type = _mod_merge.Merge3Merger
1148
 
 
1149
 
 
1150
 
class TestWeaveMerge(TestCaseWithTransport, TestMergeImplementation):
1151
 
 
1152
 
    merge_type = _mod_merge.WeaveMerger
1153
 
 
1154
 
 
1155
 
class TestLCAMerge(TestCaseWithTransport, TestMergeImplementation):
1156
 
 
1157
 
    merge_type = _mod_merge.LCAMerger
1158
 
 
1159
 
    def test_merge_move_and_change(self):
1160
 
        self.expectFailure("lca merge doesn't conflict for move and change",
1161
 
            super(TestLCAMerge, self).test_merge_move_and_change)
1162
 
 
1163
 
 
1164
1144
class LoggingMerger(object):
1165
1145
    # These seem to be the required attributes
1166
1146
    requires_base = False
1232
1212
 
1233
1213
class TestMergerInMemory(TestMergerBase):
1234
1214
 
 
1215
    def test_cache_trees_with_revision_ids_None(self):
 
1216
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
 
1217
        original_cache = dict(merger._cached_trees)
 
1218
        merger.cache_trees_with_revision_ids([None])
 
1219
        self.assertEqual(original_cache, merger._cached_trees)
 
1220
 
 
1221
    def test_cache_trees_with_revision_ids_no_revision_id(self):
 
1222
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
 
1223
        original_cache = dict(merger._cached_trees)
 
1224
        tree = self.make_branch_and_memory_tree('tree')
 
1225
        merger.cache_trees_with_revision_ids([tree])
 
1226
        self.assertEqual(original_cache, merger._cached_trees)
 
1227
 
 
1228
    def test_cache_trees_with_revision_ids_having_revision_id(self):
 
1229
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
 
1230
        original_cache = dict(merger._cached_trees)
 
1231
        tree = merger.this_branch.repository.revision_tree('B-id')
 
1232
        original_cache['B-id'] = tree
 
1233
        merger.cache_trees_with_revision_ids([tree])
 
1234
        self.assertEqual(original_cache, merger._cached_trees)
 
1235
 
1235
1236
    def test_find_base(self):
1236
1237
        merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1237
1238
        self.assertEqual('A-id', merger.base_rev_id)