867
865
b'.*a-file(.|\n)*b-file')
870
class TestPatienceDiffLib(tests.TestCase):
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
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))
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),
900
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
902
def test_recurse_matches(self):
903
def test_one(a, b, matches):
905
self._recurse_matches(
906
a, b, 0, 0, len(a), len(b), test_matches, 10)
907
self.assertEqual(test_matches, matches)
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
916
test_one('abcdbce', 'afbcgdbce', [(0, 0), (1, 2), (2, 3), (3, 5),
917
(4, 6), (5, 7), (6, 8)])
919
# recurse_matches doesn't match non-unique
920
# lines surrounded by bogus text.
921
# The update has been done in patiencediff.SequenceMatcher instead
923
# This is what it could be
924
#test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
926
# This is what it currently gives:
927
test_one('aBccDe', 'abccde', [(0, 0), (5, 5)])
929
def assertDiffBlocks(self, a, b, expected_blocks):
930
"""Check that the sequence matcher returns the correct blocks.
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)
937
matcher = self._PatienceSequenceMatcher(None, a, b)
938
blocks = matcher.get_matching_blocks()
940
self.assertEqual((len(a), len(b), 0), last)
941
self.assertEqual(expected_blocks, blocks)
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,
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)])
964
# make sure it supports passing in lists
965
self.assertDiffBlocks(
968
'how are you today?\n'],
970
'how are you today?\n'],
971
[(0, 0, 1), (2, 1, 1)])
973
# non unique lines surrounded by non-matching lines
975
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
977
# But they only need to be locally unique
978
self.assertDiffBlocks('aBcDec', 'abcdec', [
979
(0, 0, 1), (2, 2, 1), (4, 4, 2)])
981
# non unique blocks won't be matched
982
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
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)])
988
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
989
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
990
self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
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,')],
1000
self.assertDiffBlocks([('a',), ('b',), ('c,')],
1001
[('a',), ('b',), ('d,')],
1003
self.assertDiffBlocks([('d',), ('b',), ('c,')],
1004
[('a',), ('b',), ('c,')],
1006
self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
1007
[('a',), ('b',), ('c,')],
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)])
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())
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)
1029
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1030
('equal', 1, 4, 0, 3),
1031
('insert', 4, 4, 3, 4)
1033
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1034
('equal', 1, 5, 0, 4)
1036
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1037
('replace', 2, 3, 2, 3),
1038
('equal', 3, 5, 3, 5)
1040
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1041
('replace', 2, 3, 2, 5),
1042
('equal', 3, 5, 5, 7)
1044
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1045
('insert', 2, 2, 2, 5),
1046
('equal', 2, 4, 5, 7)
1048
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1049
[('equal', 0, 6, 0, 6),
1050
('insert', 6, 6, 6, 11),
1051
('equal', 6, 16, 11, 21)
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),
1060
chk_ops('aBccDe', 'abccde',
1061
[('equal', 0, 1, 0, 1),
1062
('replace', 1, 5, 1, 5),
1063
('equal', 5, 6, 5, 6),
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),
1072
chk_ops('aBcdEcdFg', 'abcdecdfg',
1073
[('equal', 0, 1, 0, 1),
1074
('replace', 1, 8, 1, 8),
1075
('equal', 8, 9, 8, 9)
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)
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)))
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)
1102
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1103
('equal', 1, 4, 0, 3),
1104
('insert', 4, 4, 3, 4)
1106
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1107
[[('equal', 3, 6, 3, 6),
1108
('insert', 6, 6, 6, 11),
1109
('equal', 6, 9, 11, 14)
1111
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1112
[[('equal', 2, 6, 2, 6),
1113
('insert', 6, 6, 6, 11),
1114
('equal', 6, 10, 11, 15)
1116
chk_ops('Xabcdef', 'abcdef',
1117
[[('delete', 0, 1, 0, 0),
1118
('equal', 1, 4, 0, 3)
1120
chk_ops('abcdef', 'abcdefX',
1121
[[('equal', 3, 6, 3, 6),
1122
('insert', 6, 6, 6, 7)
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)])
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)])
1136
# These are rot13 code snippets.
1137
self.assertDiffBlocks('''\
1138
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1140
gnxrf_netf = ['svyr*']
1141
gnxrf_bcgvbaf = ['ab-erphefr']
1143
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1144
sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1146
ercbegre = nqq_ercbegre_ahyy
1148
ercbegre = nqq_ercbegre_cevag
1149
fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
1152
pynff pzq_zxqve(Pbzznaq):
1153
'''.splitlines(True), '''\
1154
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1156
--qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1159
gnxrf_netf = ['svyr*']
1160
gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
1162
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
1167
# Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
1168
npgvba = omeyvo.nqq.nqq_npgvba_ahyy
1170
npgvba = omeyvo.nqq.nqq_npgvba_cevag
1172
npgvba = omeyvo.nqq.nqq_npgvba_nqq
1174
npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1176
omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1179
pynff pzq_zxqve(Pbzznaq):
1180
'''.splitlines(True), [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1182
def test_patience_unified_diff(self):
1183
txt_a = ['hello there\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',
1192
'@@ -1,3 +1,2 @@\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',
1203
'@@ -1,6 +1,11 @@\n',
1214
' f\n'], list(unified_diff(txt_a, txt_b)))
1215
# And the patience diff
1216
self.assertEqual(['--- \n',
1218
'@@ -4,6 +4,11 @@\n',
1230
], list(unified_diff(txt_a, txt_b,
1231
sequencematcher=psm)))
1233
def test_patience_unified_diff_with_dates(self):
1234
txt_a = ['hello there\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',
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)))
1254
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1256
_test_needs_features = [features.compiled_patiencediff_feature]
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
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,
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', []])
1280
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1283
super(TestPatienceDiffLibFiles, self).setUp()
1284
self._PatienceSequenceMatcher = \
1285
_patiencediff_py.PatienceSequenceMatcher_py
1287
def test_patience_unified_diff_files(self):
1288
txt_a = [b'hello there\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:
1295
with open('b1', 'wb') as f:
1298
unified_diff_files = patiencediff.unified_diff_files
1299
psm = self._PatienceSequenceMatcher
1300
self.assertEqual([b'--- a1\n',
1302
b'@@ -1,3 +1,2 @@\n',
1305
b' how are you today?\n',
1306
], list(unified_diff_files(b'a1', b'b1',
1307
sequencematcher=psm)))
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:
1313
with open('b2', 'wt') as f:
1316
# This is the result with LongestCommonSubstring matching
1317
self.assertEqual([b'--- a2\n',
1319
b'@@ -1,6 +1,11 @@\n',
1330
b' f\n'], list(unified_diff_files(b'a2', b'b2')))
1332
# And the patience diff
1333
self.assertEqual([b'--- a2\n',
1335
b'@@ -4,6 +4,11 @@\n',
1347
list(unified_diff_files(b'a2', b'b2',
1348
sequencematcher=psm)))
1351
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1353
_test_needs_features = [features.compiled_patiencediff_feature]
1356
super(TestPatienceDiffLibFiles_c, self).setUp()
1357
from breezy import _patiencediff_c
1358
self._PatienceSequenceMatcher = \
1359
_patiencediff_c.PatienceSequenceMatcher_c
1362
class TestUsingCompiledIfAvailable(tests.TestCase):
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)
1370
from breezy._patiencediff_py import PatienceSequenceMatcher_py
1371
self.assertIs(PatienceSequenceMatcher_py,
1372
patiencediff.PatienceSequenceMatcher)
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)
1380
from breezy._patiencediff_py import unique_lcs_py
1381
self.assertIs(unique_lcs_py,
1382
patiencediff.unique_lcs)
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)
1390
from breezy._patiencediff_py import recurse_matches_py
1391
self.assertIs(recurse_matches_py,
1392
patiencediff.recurse_matches)
1395
868
class TestDiffFromTool(tests.TestCaseWithTransport):
1397
870
def test_from_string(self):