/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 breezy/tests/test_diff.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-05 07:32:38 UTC
  • mto: (7290.1.21 work)
  • mto: This revision was merged to the branch mainline in revision 7311.
  • Revision ID: jelmer@jelmer.uk-20190305073238-zlqn981opwnqsmzi
Add appveyor configuration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
import contextlib
18
 
from io import BytesIO
19
17
import os
20
18
import re
21
19
import subprocess
26
24
    diff,
27
25
    errors,
28
26
    osutils,
 
27
    patiencediff,
 
28
    _patiencediff_py,
29
29
    revision as _mod_revision,
30
30
    revisionspec,
31
31
    revisiontree,
32
32
    tests,
 
33
    transform,
 
34
    )
 
35
from ..sixish import (
 
36
    BytesIO,
 
37
    unichr,
33
38
    )
34
39
from ..tests import (
35
40
    features,
559
564
    def test_internal_diff_exec_property(self):
560
565
        tree = self.make_branch_and_tree('tree')
561
566
 
562
 
        tt = tree.transform()
 
567
        tt = transform.TreeTransform(tree)
563
568
        tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
564
569
        tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
565
570
        tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
569
574
        tt.apply()
570
575
        tree.commit('one', rev_id=b'rev-1')
571
576
 
572
 
        tt = tree.transform()
 
577
        tt = transform.TreeTransform(tree)
573
578
        tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
574
579
        tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
575
580
        tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
605
610
            [('tree/' + alpha, b'\0'),
606
611
             ('tree/' + omega,
607
612
              (b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
608
 
        tree.add([alpha])
609
 
        tree.add([omega])
 
613
        tree.add([alpha], [b'file-id'])
 
614
        tree.add([omega], [b'file-id-2'])
610
615
        diff_content = StubO()
611
616
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
612
617
        diff_content.check_types(self, bytes)
802
807
        self.differ.diff('olddir/oldfile', 'newdir/newfile')
803
808
        self.assertContainsRe(
804
809
            self.differ.to_file.getvalue(),
805
 
            br'--- olddir/oldfile.*\n'
806
 
            br'\+\+\+ newdir/newfile.*\n'
807
 
            br'\@\@ -1,1 \+0,0 \@\@\n'
808
 
            br'-old\n'
809
 
            br'\n')
 
810
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
 
811
            br' \@\@\n-old\n\n')
810
812
        self.assertContainsRe(self.differ.to_file.getvalue(),
811
813
                              b"=== target is 'new'\n")
812
814
 
865
867
                              b'.*a-file(.|\n)*b-file')
866
868
 
867
869
 
 
870
class TestPatienceDiffLib(tests.TestCase):
 
871
 
 
872
    def setUp(self):
 
873
        super(TestPatienceDiffLib, self).setUp()
 
874
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
875
        self._recurse_matches = _patiencediff_py.recurse_matches_py
 
876
        self._PatienceSequenceMatcher = \
 
877
            _patiencediff_py.PatienceSequenceMatcher_py
 
878
 
 
879
    def test_diff_unicode_string(self):
 
880
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
 
881
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
 
882
        sm = self._PatienceSequenceMatcher(None, a, b)
 
883
        mb = sm.get_matching_blocks()
 
884
        self.assertEqual(35, len(mb))
 
885
 
 
886
    def test_unique_lcs(self):
 
887
        unique_lcs = self._unique_lcs
 
888
        self.assertEqual(unique_lcs('', ''), [])
 
889
        self.assertEqual(unique_lcs('', 'a'), [])
 
890
        self.assertEqual(unique_lcs('a', ''), [])
 
891
        self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
 
892
        self.assertEqual(unique_lcs('a', 'b'), [])
 
893
        self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
 
894
        self.assertEqual(unique_lcs('abcde', 'cdeab'),
 
895
                         [(2, 0), (3, 1), (4, 2)])
 
896
        self.assertEqual(unique_lcs('cdeab', 'abcde'),
 
897
                         [(0, 2), (1, 3), (2, 4)])
 
898
        self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
 
899
                                                        (3, 3), (4, 4)])
 
900
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
 
901
 
 
902
    def test_recurse_matches(self):
 
903
        def test_one(a, b, matches):
 
904
            test_matches = []
 
905
            self._recurse_matches(
 
906
                a, b, 0, 0, len(a), len(b), test_matches, 10)
 
907
            self.assertEqual(test_matches, matches)
 
908
 
 
909
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
 
910
                 [(0, 0), (2, 2), (4, 4)])
 
911
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
 
912
                 [(0, 0), (2, 1), (4, 2)])
 
913
        # Even though 'bc' is not unique globally, and is surrounded by
 
914
        # non-matching lines, we should still match, because they are locally
 
915
        # unique
 
916
        test_one('abcdbce', 'afbcgdbce', [(0, 0), (1, 2), (2, 3), (3, 5),
 
917
                                          (4, 6), (5, 7), (6, 8)])
 
918
 
 
919
        # recurse_matches doesn't match non-unique
 
920
        # lines surrounded by bogus text.
 
921
        # The update has been done in patiencediff.SequenceMatcher instead
 
922
 
 
923
        # This is what it could be
 
924
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
 
925
 
 
926
        # This is what it currently gives:
 
927
        test_one('aBccDe', 'abccde', [(0, 0), (5, 5)])
 
928
 
 
929
    def assertDiffBlocks(self, a, b, expected_blocks):
 
930
        """Check that the sequence matcher returns the correct blocks.
 
931
 
 
932
        :param a: A sequence to match
 
933
        :param b: Another sequence to match
 
934
        :param expected_blocks: The expected output, not including the final
 
935
            matching block (len(a), len(b), 0)
 
936
        """
 
937
        matcher = self._PatienceSequenceMatcher(None, a, b)
 
938
        blocks = matcher.get_matching_blocks()
 
939
        last = blocks.pop()
 
940
        self.assertEqual((len(a), len(b), 0), last)
 
941
        self.assertEqual(expected_blocks, blocks)
 
942
 
 
943
    def test_matching_blocks(self):
 
944
        # Some basic matching tests
 
945
        self.assertDiffBlocks('', '', [])
 
946
        self.assertDiffBlocks([], [], [])
 
947
        self.assertDiffBlocks('abc', '', [])
 
948
        self.assertDiffBlocks('', 'abc', [])
 
949
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
 
950
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
 
951
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
 
952
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
 
953
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
954
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
955
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
956
        # This may check too much, but it checks to see that
 
957
        # a copied block stays attached to the previous section,
 
958
        # not the later one.
 
959
        # difflib would tend to grab the trailing longest match
 
960
        # which would make the diff not look right
 
961
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
962
                              [(0, 0, 6), (6, 11, 10)])
 
963
 
 
964
        # make sure it supports passing in lists
 
965
        self.assertDiffBlocks(
 
966
            ['hello there\n',
 
967
             'world\n',
 
968
             'how are you today?\n'],
 
969
            ['hello there\n',
 
970
             'how are you today?\n'],
 
971
            [(0, 0, 1), (2, 1, 1)])
 
972
 
 
973
        # non unique lines surrounded by non-matching lines
 
974
        # won't be found
 
975
        self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
 
976
 
 
977
        # But they only need to be locally unique
 
978
        self.assertDiffBlocks('aBcDec', 'abcdec', [
 
979
                              (0, 0, 1), (2, 2, 1), (4, 4, 2)])
 
980
 
 
981
        # non unique blocks won't be matched
 
982
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
 
983
 
 
984
        # but locally unique ones will
 
985
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
 
986
                                                           (5, 4, 1), (7, 5, 2), (10, 8, 1)])
 
987
 
 
988
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
 
989
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
 
990
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
 
991
 
 
992
    def test_matching_blocks_tuples(self):
 
993
        # Some basic matching tests
 
994
        self.assertDiffBlocks([], [], [])
 
995
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
 
996
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
 
997
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
 
998
                              [('a',), ('b',), ('c,')],
 
999
                              [(0, 0, 3)])
 
1000
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
 
1001
                              [('a',), ('b',), ('d,')],
 
1002
                              [(0, 0, 2)])
 
1003
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
 
1004
                              [('a',), ('b',), ('c,')],
 
1005
                              [(1, 1, 2)])
 
1006
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
 
1007
                              [('a',), ('b',), ('c,')],
 
1008
                              [(1, 0, 3)])
 
1009
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
 
1010
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
 
1011
                              [(0, 0, 1), (2, 2, 1)])
 
1012
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
 
1013
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
 
1014
                              [(0, 0, 1), (2, 2, 1)])
 
1015
 
 
1016
    def test_opcodes(self):
 
1017
        def chk_ops(a, b, expected_codes):
 
1018
            s = self._PatienceSequenceMatcher(None, a, b)
 
1019
            self.assertEqual(expected_codes, s.get_opcodes())
 
1020
 
 
1021
        chk_ops('', '', [])
 
1022
        chk_ops([], [], [])
 
1023
        chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
 
1024
        chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
 
1025
        chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
 
1026
        chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
 
1027
                                 ('replace', 3, 4, 3, 4)
 
1028
                                 ])
 
1029
        chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
 
1030
                                 ('equal', 1, 4, 0, 3),
 
1031
                                 ('insert', 4, 4, 3, 4)
 
1032
                                 ])
 
1033
        chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
 
1034
                                  ('equal', 1, 5, 0, 4)
 
1035
                                  ])
 
1036
        chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
 
1037
                                   ('replace', 2, 3, 2, 3),
 
1038
                                   ('equal', 3, 5, 3, 5)
 
1039
                                   ])
 
1040
        chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
 
1041
                                     ('replace', 2, 3, 2, 5),
 
1042
                                     ('equal', 3, 5, 5, 7)
 
1043
                                     ])
 
1044
        chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
 
1045
                                    ('insert', 2, 2, 2, 5),
 
1046
                                    ('equal', 2, 4, 5, 7)
 
1047
                                    ])
 
1048
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1049
                [('equal', 0, 6, 0, 6),
 
1050
                 ('insert', 6, 6, 6, 11),
 
1051
                 ('equal', 6, 16, 11, 21)
 
1052
                 ])
 
1053
        chk_ops(
 
1054
            ['hello there\n', 'world\n', 'how are you today?\n'],
 
1055
            ['hello there\n', 'how are you today?\n'],
 
1056
            [('equal', 0, 1, 0, 1),
 
1057
             ('delete', 1, 2, 1, 1),
 
1058
             ('equal', 2, 3, 1, 2),
 
1059
             ])
 
1060
        chk_ops('aBccDe', 'abccde',
 
1061
                [('equal', 0, 1, 0, 1),
 
1062
                 ('replace', 1, 5, 1, 5),
 
1063
                 ('equal', 5, 6, 5, 6),
 
1064
                 ])
 
1065
        chk_ops('aBcDec', 'abcdec',
 
1066
                [('equal', 0, 1, 0, 1),
 
1067
                 ('replace', 1, 2, 1, 2),
 
1068
                 ('equal', 2, 3, 2, 3),
 
1069
                 ('replace', 3, 4, 3, 4),
 
1070
                 ('equal', 4, 6, 4, 6),
 
1071
                 ])
 
1072
        chk_ops('aBcdEcdFg', 'abcdecdfg',
 
1073
                [('equal', 0, 1, 0, 1),
 
1074
                 ('replace', 1, 8, 1, 8),
 
1075
                 ('equal', 8, 9, 8, 9)
 
1076
                 ])
 
1077
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
 
1078
                [('equal', 0, 1, 0, 1),
 
1079
                 ('replace', 1, 2, 1, 2),
 
1080
                 ('equal', 2, 4, 2, 4),
 
1081
                 ('delete', 4, 5, 4, 4),
 
1082
                 ('equal', 5, 6, 4, 5),
 
1083
                 ('delete', 6, 7, 5, 5),
 
1084
                 ('equal', 7, 9, 5, 7),
 
1085
                 ('replace', 9, 10, 7, 8),
 
1086
                 ('equal', 10, 11, 8, 9)
 
1087
                 ])
 
1088
 
 
1089
    def test_grouped_opcodes(self):
 
1090
        def chk_ops(a, b, expected_codes, n=3):
 
1091
            s = self._PatienceSequenceMatcher(None, a, b)
 
1092
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
 
1093
 
 
1094
        chk_ops('', '', [])
 
1095
        chk_ops([], [], [])
 
1096
        chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
 
1097
        chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
 
1098
        chk_ops('abcd', 'abcd', [])
 
1099
        chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
 
1100
                                  ('replace', 3, 4, 3, 4)
 
1101
                                  ]])
 
1102
        chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
 
1103
                                  ('equal', 1, 4, 0, 3),
 
1104
                                  ('insert', 4, 4, 3, 4)
 
1105
                                  ]])
 
1106
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1107
                [[('equal', 3, 6, 3, 6),
 
1108
                  ('insert', 6, 6, 6, 11),
 
1109
                  ('equal', 6, 9, 11, 14)
 
1110
                  ]])
 
1111
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1112
                [[('equal', 2, 6, 2, 6),
 
1113
                  ('insert', 6, 6, 6, 11),
 
1114
                  ('equal', 6, 10, 11, 15)
 
1115
                  ]], 4)
 
1116
        chk_ops('Xabcdef', 'abcdef',
 
1117
                [[('delete', 0, 1, 0, 0),
 
1118
                  ('equal', 1, 4, 0, 3)
 
1119
                  ]])
 
1120
        chk_ops('abcdef', 'abcdefX',
 
1121
                [[('equal', 3, 6, 3, 6),
 
1122
                  ('insert', 6, 6, 6, 7)
 
1123
                  ]])
 
1124
 
 
1125
    def test_multiple_ranges(self):
 
1126
        # There was an earlier bug where we used a bad set of ranges,
 
1127
        # this triggers that specific bug, to make sure it doesn't regress
 
1128
        self.assertDiffBlocks('abcdefghijklmnop',
 
1129
                              'abcXghiYZQRSTUVWXYZijklmnop',
 
1130
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
1131
 
 
1132
        self.assertDiffBlocks('ABCd efghIjk  L',
 
1133
                              'AxyzBCn mo pqrstuvwI1 2  L',
 
1134
                              [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1135
 
 
1136
        # These are rot13 code snippets.
 
1137
        self.assertDiffBlocks('''\
 
1138
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
 
1139
    """
 
1140
    gnxrf_netf = ['svyr*']
 
1141
    gnxrf_bcgvbaf = ['ab-erphefr']
 
1142
 
 
1143
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
 
1144
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
 
1145
        vs vf_dhvrg():
 
1146
            ercbegre = nqq_ercbegre_ahyy
 
1147
        ryfr:
 
1148
            ercbegre = nqq_ercbegre_cevag
 
1149
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
 
1150
 
 
1151
 
 
1152
pynff pzq_zxqve(Pbzznaq):
 
1153
'''.splitlines(True), '''\
 
1154
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
 
1155
 
 
1156
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
 
1157
    nqq gurz.
 
1158
    """
 
1159
    gnxrf_netf = ['svyr*']
 
1160
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
 
1161
 
 
1162
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
 
1163
        vzcbeg omeyvo.nqq
 
1164
 
 
1165
        vs qel_eha:
 
1166
            vs vf_dhvrg():
 
1167
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
 
1168
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
 
1169
            ryfr:
 
1170
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
 
1171
        ryvs vf_dhvrg():
 
1172
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
 
1173
        ryfr:
 
1174
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
 
1175
 
 
1176
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
 
1177
 
 
1178
 
 
1179
pynff pzq_zxqve(Pbzznaq):
 
1180
'''.splitlines(True), [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1181
 
 
1182
    def test_patience_unified_diff(self):
 
1183
        txt_a = ['hello there\n',
 
1184
                 'world\n',
 
1185
                 'how are you today?\n']
 
1186
        txt_b = ['hello there\n',
 
1187
                 'how are you today?\n']
 
1188
        unified_diff = patiencediff.unified_diff
 
1189
        psm = self._PatienceSequenceMatcher
 
1190
        self.assertEqual(['--- \n',
 
1191
                          '+++ \n',
 
1192
                          '@@ -1,3 +1,2 @@\n',
 
1193
                          ' hello there\n',
 
1194
                          '-world\n',
 
1195
                          ' how are you today?\n'
 
1196
                          ], list(unified_diff(txt_a, txt_b,
 
1197
                                               sequencematcher=psm)))
 
1198
        txt_a = [x + '\n' for x in 'abcdefghijklmnop']
 
1199
        txt_b = [x + '\n' for x in 'abcdefxydefghijklmnop']
 
1200
        # This is the result with LongestCommonSubstring matching
 
1201
        self.assertEqual(['--- \n',
 
1202
                          '+++ \n',
 
1203
                          '@@ -1,6 +1,11 @@\n',
 
1204
                          ' a\n',
 
1205
                          ' b\n',
 
1206
                          ' c\n',
 
1207
                          '+d\n',
 
1208
                          '+e\n',
 
1209
                          '+f\n',
 
1210
                          '+x\n',
 
1211
                          '+y\n',
 
1212
                          ' d\n',
 
1213
                          ' e\n',
 
1214
                          ' f\n'], list(unified_diff(txt_a, txt_b)))
 
1215
        # And the patience diff
 
1216
        self.assertEqual(['--- \n',
 
1217
                          '+++ \n',
 
1218
                          '@@ -4,6 +4,11 @@\n',
 
1219
                          ' d\n',
 
1220
                          ' e\n',
 
1221
                          ' f\n',
 
1222
                          '+x\n',
 
1223
                          '+y\n',
 
1224
                          '+d\n',
 
1225
                          '+e\n',
 
1226
                          '+f\n',
 
1227
                          ' g\n',
 
1228
                          ' h\n',
 
1229
                          ' i\n',
 
1230
                          ], list(unified_diff(txt_a, txt_b,
 
1231
                                               sequencematcher=psm)))
 
1232
 
 
1233
    def test_patience_unified_diff_with_dates(self):
 
1234
        txt_a = ['hello there\n',
 
1235
                 'world\n',
 
1236
                 'how are you today?\n']
 
1237
        txt_b = ['hello there\n',
 
1238
                 'how are you today?\n']
 
1239
        unified_diff = patiencediff.unified_diff
 
1240
        psm = self._PatienceSequenceMatcher
 
1241
        self.assertEqual(['--- a\t2008-08-08\n',
 
1242
                          '+++ b\t2008-09-09\n',
 
1243
                          '@@ -1,3 +1,2 @@\n',
 
1244
                          ' hello there\n',
 
1245
                          '-world\n',
 
1246
                          ' how are you today?\n'
 
1247
                          ], list(unified_diff(txt_a, txt_b,
 
1248
                                               fromfile='a', tofile='b',
 
1249
                                               fromfiledate='2008-08-08',
 
1250
                                               tofiledate='2008-09-09',
 
1251
                                               sequencematcher=psm)))
 
1252
 
 
1253
 
 
1254
class TestPatienceDiffLib_c(TestPatienceDiffLib):
 
1255
 
 
1256
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1257
 
 
1258
    def setUp(self):
 
1259
        super(TestPatienceDiffLib_c, self).setUp()
 
1260
        from breezy import _patiencediff_c
 
1261
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1262
        self._recurse_matches = _patiencediff_c.recurse_matches_c
 
1263
        self._PatienceSequenceMatcher = \
 
1264
            _patiencediff_c.PatienceSequenceMatcher_c
 
1265
 
 
1266
    def test_unhashable(self):
 
1267
        """We should get a proper exception here."""
 
1268
        # We need to be able to hash items in the sequence, lists are
 
1269
        # unhashable, and thus cannot be diffed
 
1270
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1271
                              None, [[]], [])
 
1272
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1273
                              None, ['valid', []], [])
 
1274
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1275
                              None, ['valid'], [[]])
 
1276
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1277
                              None, ['valid'], ['valid', []])
 
1278
 
 
1279
 
 
1280
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1281
 
 
1282
    def setUp(self):
 
1283
        super(TestPatienceDiffLibFiles, self).setUp()
 
1284
        self._PatienceSequenceMatcher = \
 
1285
            _patiencediff_py.PatienceSequenceMatcher_py
 
1286
 
 
1287
    def test_patience_unified_diff_files(self):
 
1288
        txt_a = [b'hello there\n',
 
1289
                 b'world\n',
 
1290
                 b'how are you today?\n']
 
1291
        txt_b = [b'hello there\n',
 
1292
                 b'how are you today?\n']
 
1293
        with open('a1', 'wb') as f:
 
1294
            f.writelines(txt_a)
 
1295
        with open('b1', 'wb') as f:
 
1296
            f.writelines(txt_b)
 
1297
 
 
1298
        unified_diff_files = patiencediff.unified_diff_files
 
1299
        psm = self._PatienceSequenceMatcher
 
1300
        self.assertEqual([b'--- a1\n',
 
1301
                          b'+++ b1\n',
 
1302
                          b'@@ -1,3 +1,2 @@\n',
 
1303
                          b' hello there\n',
 
1304
                          b'-world\n',
 
1305
                          b' how are you today?\n',
 
1306
                          ], list(unified_diff_files(b'a1', b'b1',
 
1307
                                                     sequencematcher=psm)))
 
1308
 
 
1309
        txt_a = [x + '\n' for x in 'abcdefghijklmnop']
 
1310
        txt_b = [x + '\n' for x in 'abcdefxydefghijklmnop']
 
1311
        with open('a2', 'wt') as f:
 
1312
            f.writelines(txt_a)
 
1313
        with open('b2', 'wt') as f:
 
1314
            f.writelines(txt_b)
 
1315
 
 
1316
        # This is the result with LongestCommonSubstring matching
 
1317
        self.assertEqual([b'--- a2\n',
 
1318
                          b'+++ b2\n',
 
1319
                          b'@@ -1,6 +1,11 @@\n',
 
1320
                          b' a\n',
 
1321
                          b' b\n',
 
1322
                          b' c\n',
 
1323
                          b'+d\n',
 
1324
                          b'+e\n',
 
1325
                          b'+f\n',
 
1326
                          b'+x\n',
 
1327
                          b'+y\n',
 
1328
                          b' d\n',
 
1329
                          b' e\n',
 
1330
                          b' f\n'], list(unified_diff_files(b'a2', b'b2')))
 
1331
 
 
1332
        # And the patience diff
 
1333
        self.assertEqual([b'--- a2\n',
 
1334
                          b'+++ b2\n',
 
1335
                          b'@@ -4,6 +4,11 @@\n',
 
1336
                          b' d\n',
 
1337
                          b' e\n',
 
1338
                          b' f\n',
 
1339
                          b'+x\n',
 
1340
                          b'+y\n',
 
1341
                          b'+d\n',
 
1342
                          b'+e\n',
 
1343
                          b'+f\n',
 
1344
                          b' g\n',
 
1345
                          b' h\n',
 
1346
                          b' i\n'],
 
1347
                         list(unified_diff_files(b'a2', b'b2',
 
1348
                                                 sequencematcher=psm)))
 
1349
 
 
1350
 
 
1351
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
 
1352
 
 
1353
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1354
 
 
1355
    def setUp(self):
 
1356
        super(TestPatienceDiffLibFiles_c, self).setUp()
 
1357
        from breezy import _patiencediff_c
 
1358
        self._PatienceSequenceMatcher = \
 
1359
            _patiencediff_c.PatienceSequenceMatcher_c
 
1360
 
 
1361
 
 
1362
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1363
 
 
1364
    def test_PatienceSequenceMatcher(self):
 
1365
        if features.compiled_patiencediff_feature.available():
 
1366
            from breezy._patiencediff_c import PatienceSequenceMatcher_c
 
1367
            self.assertIs(PatienceSequenceMatcher_c,
 
1368
                          patiencediff.PatienceSequenceMatcher)
 
1369
        else:
 
1370
            from breezy._patiencediff_py import PatienceSequenceMatcher_py
 
1371
            self.assertIs(PatienceSequenceMatcher_py,
 
1372
                          patiencediff.PatienceSequenceMatcher)
 
1373
 
 
1374
    def test_unique_lcs(self):
 
1375
        if features.compiled_patiencediff_feature.available():
 
1376
            from breezy._patiencediff_c import unique_lcs_c
 
1377
            self.assertIs(unique_lcs_c,
 
1378
                          patiencediff.unique_lcs)
 
1379
        else:
 
1380
            from breezy._patiencediff_py import unique_lcs_py
 
1381
            self.assertIs(unique_lcs_py,
 
1382
                          patiencediff.unique_lcs)
 
1383
 
 
1384
    def test_recurse_matches(self):
 
1385
        if features.compiled_patiencediff_feature.available():
 
1386
            from breezy._patiencediff_c import recurse_matches_c
 
1387
            self.assertIs(recurse_matches_c,
 
1388
                          patiencediff.recurse_matches)
 
1389
        else:
 
1390
            from breezy._patiencediff_py import recurse_matches_py
 
1391
            self.assertIs(recurse_matches_py,
 
1392
                          patiencediff.recurse_matches)
 
1393
 
 
1394
 
868
1395
class TestDiffFromTool(tests.TestCaseWithTransport):
869
1396
 
870
1397
    def test_from_string(self):
871
 
        diff_obj = diff.DiffFromTool.from_string(
872
 
            ['diff', '{old_path}', '{new_path}'],
873
 
            None, None, None)
874
 
        self.addCleanup(diff_obj.finish)
875
 
        self.assertEqual(['diff', '{old_path}', '{new_path}'],
876
 
                         diff_obj.command_template)
877
 
 
878
 
    def test_from_string_no_paths(self):
879
 
        diff_obj = diff.DiffFromTool.from_string(
880
 
            ['diff', "-u5"], None, None, None)
881
 
        self.addCleanup(diff_obj.finish)
882
 
        self.assertEqual(['diff', '-u5'],
883
 
                         diff_obj.command_template)
884
 
        self.assertEqual(['diff', '-u5', 'old-path', 'new-path'],
885
 
                         diff_obj._get_command('old-path', 'new-path'))
 
1398
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
 
1399
        self.addCleanup(diff_obj.finish)
 
1400
        self.assertEqual(['diff', '@old_path', '@new_path'],
 
1401
                         diff_obj.command_template)
886
1402
 
887
1403
    def test_from_string_u5(self):
888
 
        diff_obj = diff.DiffFromTool.from_string(
889
 
            ['diff', "-u 5", '{old_path}', '{new_path}'], None, None, None)
 
1404
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1405
                                                 None, None, None)
890
1406
        self.addCleanup(diff_obj.finish)
891
 
        self.assertEqual(['diff', '-u 5', '{old_path}', '{new_path}'],
 
1407
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
892
1408
                         diff_obj.command_template)
893
1409
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
894
1410
                         diff_obj._get_command('old-path', 'new-path'))
895
1411
 
896
1412
    def test_from_string_path_with_backslashes(self):
897
1413
        self.requireFeature(features.backslashdir_feature)
898
 
        tool = ['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}']
 
1414
        tool = 'C:\\Tools\\Diff.exe'
899
1415
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
900
1416
        self.addCleanup(diff_obj.finish)
901
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}'],
 
1417
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
902
1418
                         diff_obj.command_template)
903
1419
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
904
1420
                         diff_obj._get_command('old-path', 'new-path'))
906
1422
    def test_execute(self):
907
1423
        output = BytesIO()
908
1424
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
909
 
                                      'print("{old_path} {new_path}")'],
 
1425
                                      'print("@old_path @new_path")'],
910
1426
                                     None, None, output)
911
1427
        self.addCleanup(diff_obj.finish)
912
1428
        diff_obj._execute('old', 'new')
934
1450
        basis_tree.lock_read()
935
1451
        self.addCleanup(basis_tree.unlock)
936
1452
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
937
 
                                      'print "{old_path} {new_path}"'],
 
1453
                                      'print "@old_path @new_path"'],
938
1454
                                     basis_tree, tree, output)
939
1455
        diff_obj._prepare_files('file', 'file', file_id=b'file-id')
940
1456
        # The old content should be readonly
970
1486
        tree.lock_read()
971
1487
        self.addCleanup(tree.unlock)
972
1488
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
973
 
                                      'print "{old_path} {new_path}"'],
 
1489
                                      'print "@old_path @new_path"'],
974
1490
                                     old_tree, tree, output)
975
1491
        self.addCleanup(diff_obj.finish)
976
1492
        self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
992
1508
    def test_encodable_filename(self):
993
1509
        # Just checks file path for external diff tool.
994
1510
        # We cannot change CPython's internal encoding used by os.exec*.
995
 
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
 
1511
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
996
1512
                                    None, None, None)
997
1513
        for _, scenario in EncodingAdapter.encoding_scenarios:
998
1514
            encoding = scenario['encoding']
1007
1523
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1008
1524
 
1009
1525
    def test_unencodable_filename(self):
1010
 
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
 
1526
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1011
1527
                                    None, None, None)
1012
1528
        for _, scenario in EncodingAdapter.encoding_scenarios:
1013
1529
            encoding = scenario['encoding']
1031
1547
 
1032
1548
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1033
1549
        """Call get_trees_and_branches_to_diff_locked."""
1034
 
        exit_stack = contextlib.ExitStack()
1035
 
        self.addCleanup(exit_stack.close)
1036
1550
        return diff.get_trees_and_branches_to_diff_locked(
1037
 
            path_list, revision_specs, old_url, new_url, exit_stack)
 
1551
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1038
1552
 
1039
1553
    def test_basic(self):
1040
1554
        tree = self.make_branch_and_tree('tree')