870
862
self.old_tree.add('b-file')
871
863
self.differ.show_diff(None)
872
864
self.assertContainsRe(self.differ.to_file.getvalue(),
873
b'.*a-file(.|\n)*b-file')
876
class TestPatienceDiffLib(tests.TestCase):
879
super(TestPatienceDiffLib, self).setUp()
880
self._unique_lcs = _patiencediff_py.unique_lcs_py
881
self._recurse_matches = _patiencediff_py.recurse_matches_py
882
self._PatienceSequenceMatcher = \
883
_patiencediff_py.PatienceSequenceMatcher_py
885
def test_diff_unicode_string(self):
886
a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
887
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
888
sm = self._PatienceSequenceMatcher(None, a, b)
889
mb = sm.get_matching_blocks()
890
self.assertEqual(35, len(mb))
892
def test_unique_lcs(self):
893
unique_lcs = self._unique_lcs
894
self.assertEqual(unique_lcs('', ''), [])
895
self.assertEqual(unique_lcs('', 'a'), [])
896
self.assertEqual(unique_lcs('a', ''), [])
897
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
898
self.assertEqual(unique_lcs('a', 'b'), [])
899
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
900
self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
901
self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
902
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
904
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
906
def test_recurse_matches(self):
907
def test_one(a, b, matches):
909
self._recurse_matches(
910
a, b, 0, 0, len(a), len(b), test_matches, 10)
911
self.assertEqual(test_matches, matches)
913
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
914
[(0, 0), (2, 2), (4, 4)])
915
test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
916
[(0, 0), (2, 1), (4, 2)])
917
# Even though 'bc' is not unique globally, and is surrounded by
918
# non-matching lines, we should still match, because they are locally
920
test_one('abcdbce', 'afbcgdbce', [(0, 0), (1, 2), (2, 3), (3, 5),
921
(4, 6), (5, 7), (6, 8)])
923
# recurse_matches doesn't match non-unique
924
# lines surrounded by bogus text.
925
# The update has been done in patiencediff.SequenceMatcher instead
927
# This is what it could be
928
#test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
930
# This is what it currently gives:
931
test_one('aBccDe', 'abccde', [(0, 0), (5, 5)])
933
def assertDiffBlocks(self, a, b, expected_blocks):
934
"""Check that the sequence matcher returns the correct blocks.
936
:param a: A sequence to match
937
:param b: Another sequence to match
938
:param expected_blocks: The expected output, not including the final
939
matching block (len(a), len(b), 0)
941
matcher = self._PatienceSequenceMatcher(None, a, b)
942
blocks = matcher.get_matching_blocks()
944
self.assertEqual((len(a), len(b), 0), last)
945
self.assertEqual(expected_blocks, blocks)
947
def test_matching_blocks(self):
948
# Some basic matching tests
949
self.assertDiffBlocks('', '', [])
950
self.assertDiffBlocks([], [], [])
951
self.assertDiffBlocks('abc', '', [])
952
self.assertDiffBlocks('', 'abc', [])
953
self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
954
self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
955
self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
956
self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
957
self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
958
self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
959
self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
960
# This may check too much, but it checks to see that
961
# a copied block stays attached to the previous section,
963
# difflib would tend to grab the trailing longest match
964
# which would make the diff not look right
965
self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
966
[(0, 0, 6), (6, 11, 10)])
968
# make sure it supports passing in lists
969
self.assertDiffBlocks(
972
'how are you today?\n'],
974
'how are you today?\n'],
975
[(0, 0, 1), (2, 1, 1)])
977
# non unique lines surrounded by non-matching lines
979
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
981
# But they only need to be locally unique
982
self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
984
# non unique blocks won't be matched
985
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
987
# but locally unique ones will
988
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
989
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
991
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
992
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
993
self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
995
def test_matching_blocks_tuples(self):
996
# Some basic matching tests
997
self.assertDiffBlocks([], [], [])
998
self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
999
self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
1000
self.assertDiffBlocks([('a',), ('b',), ('c,')],
1001
[('a',), ('b',), ('c,')],
1003
self.assertDiffBlocks([('a',), ('b',), ('c,')],
1004
[('a',), ('b',), ('d,')],
1006
self.assertDiffBlocks([('d',), ('b',), ('c,')],
1007
[('a',), ('b',), ('c,')],
1009
self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
1010
[('a',), ('b',), ('c,')],
1012
self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
1013
[('a', 'b'), ('c', 'X'), ('e', 'f')],
1014
[(0, 0, 1), (2, 2, 1)])
1015
self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
1016
[('a', 'b'), ('c', 'dX'), ('e', 'f')],
1017
[(0, 0, 1), (2, 2, 1)])
1019
def test_opcodes(self):
1020
def chk_ops(a, b, expected_codes):
1021
s = self._PatienceSequenceMatcher(None, a, b)
1022
self.assertEqual(expected_codes, s.get_opcodes())
1026
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1027
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1028
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1029
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1030
('replace', 3, 4, 3, 4)
1032
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1033
('equal', 1, 4, 0, 3),
1034
('insert', 4, 4, 3, 4)
1036
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1037
('equal', 1, 5, 0, 4)
1039
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1040
('replace', 2, 3, 2, 3),
1041
('equal', 3, 5, 3, 5)
1043
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1044
('replace', 2, 3, 2, 5),
1045
('equal', 3, 5, 5, 7)
1047
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1048
('insert', 2, 2, 2, 5),
1049
('equal', 2, 4, 5, 7)
1051
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1052
[('equal', 0, 6, 0, 6),
1053
('insert', 6, 6, 6, 11),
1054
('equal', 6, 16, 11, 21)
1059
, 'how are you today?\n'],
1061
, 'how are you today?\n'],
1062
[('equal', 0, 1, 0, 1),
1063
('delete', 1, 2, 1, 1),
1064
('equal', 2, 3, 1, 2),
1066
chk_ops('aBccDe', 'abccde',
1067
[('equal', 0, 1, 0, 1),
1068
('replace', 1, 5, 1, 5),
1069
('equal', 5, 6, 5, 6),
1071
chk_ops('aBcDec', 'abcdec',
1072
[('equal', 0, 1, 0, 1),
1073
('replace', 1, 2, 1, 2),
1074
('equal', 2, 3, 2, 3),
1075
('replace', 3, 4, 3, 4),
1076
('equal', 4, 6, 4, 6),
1078
chk_ops('aBcdEcdFg', 'abcdecdfg',
1079
[('equal', 0, 1, 0, 1),
1080
('replace', 1, 8, 1, 8),
1081
('equal', 8, 9, 8, 9)
1083
chk_ops('aBcdEeXcdFg', 'abcdecdfg',
1084
[('equal', 0, 1, 0, 1),
1085
('replace', 1, 2, 1, 2),
1086
('equal', 2, 4, 2, 4),
1087
('delete', 4, 5, 4, 4),
1088
('equal', 5, 6, 4, 5),
1089
('delete', 6, 7, 5, 5),
1090
('equal', 7, 9, 5, 7),
1091
('replace', 9, 10, 7, 8),
1092
('equal', 10, 11, 8, 9)
1095
def test_grouped_opcodes(self):
1096
def chk_ops(a, b, expected_codes, n=3):
1097
s = self._PatienceSequenceMatcher(None, a, b)
1098
self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
1102
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1103
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
1104
chk_ops('abcd', 'abcd', [])
1105
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1106
('replace', 3, 4, 3, 4)
1108
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1109
('equal', 1, 4, 0, 3),
1110
('insert', 4, 4, 3, 4)
1112
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1113
[[('equal', 3, 6, 3, 6),
1114
('insert', 6, 6, 6, 11),
1115
('equal', 6, 9, 11, 14)
1117
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1118
[[('equal', 2, 6, 2, 6),
1119
('insert', 6, 6, 6, 11),
1120
('equal', 6, 10, 11, 15)
1122
chk_ops('Xabcdef', 'abcdef',
1123
[[('delete', 0, 1, 0, 0),
1124
('equal', 1, 4, 0, 3)
1126
chk_ops('abcdef', 'abcdefX',
1127
[[('equal', 3, 6, 3, 6),
1128
('insert', 6, 6, 6, 7)
1132
def test_multiple_ranges(self):
1133
# There was an earlier bug where we used a bad set of ranges,
1134
# this triggers that specific bug, to make sure it doesn't regress
1135
self.assertDiffBlocks('abcdefghijklmnop',
1136
'abcXghiYZQRSTUVWXYZijklmnop',
1137
[(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1139
self.assertDiffBlocks('ABCd efghIjk L',
1140
'AxyzBCn mo pqrstuvwI1 2 L',
1141
[(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1143
# These are rot13 code snippets.
1144
self.assertDiffBlocks('''\
1145
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1147
gnxrf_netf = ['svyr*']
1148
gnxrf_bcgvbaf = ['ab-erphefr']
1150
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1151
sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1153
ercbegre = nqq_ercbegre_ahyy
1155
ercbegre = nqq_ercbegre_cevag
1156
fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
1159
pynff pzq_zxqve(Pbzznaq):
1160
'''.splitlines(True), '''\
1161
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1163
--qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1166
gnxrf_netf = ['svyr*']
1167
gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
1169
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
1174
# Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
1175
npgvba = omeyvo.nqq.nqq_npgvba_ahyy
1177
npgvba = omeyvo.nqq.nqq_npgvba_cevag
1179
npgvba = omeyvo.nqq.nqq_npgvba_nqq
1181
npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1183
omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1186
pynff pzq_zxqve(Pbzznaq):
1187
'''.splitlines(True)
1188
, [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1190
def test_patience_unified_diff(self):
1191
txt_a = ['hello there\n',
1193
'how are you today?\n']
1194
txt_b = ['hello there\n',
1195
'how are you today?\n']
1196
unified_diff = patiencediff.unified_diff
1197
psm = self._PatienceSequenceMatcher
1198
self.assertEqual(['--- \n',
1200
'@@ -1,3 +1,2 @@\n',
1203
' how are you today?\n'
1205
, list(unified_diff(txt_a, txt_b,
1206
sequencematcher=psm)))
1207
txt_a = [x+'\n' for x in 'abcdefghijklmnop']
1208
txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
1209
# This is the result with LongestCommonSubstring matching
1210
self.assertEqual(['--- \n',
1212
'@@ -1,6 +1,11 @@\n',
1224
, list(unified_diff(txt_a, txt_b)))
1225
# And the patience diff
1226
self.assertEqual(['--- \n',
1228
'@@ -4,6 +4,11 @@\n',
1241
, list(unified_diff(txt_a, txt_b,
1242
sequencematcher=psm)))
1244
def test_patience_unified_diff_with_dates(self):
1245
txt_a = ['hello there\n',
1247
'how are you today?\n']
1248
txt_b = ['hello there\n',
1249
'how are you today?\n']
1250
unified_diff = patiencediff.unified_diff
1251
psm = self._PatienceSequenceMatcher
1252
self.assertEqual(['--- a\t2008-08-08\n',
1253
'+++ b\t2008-09-09\n',
1254
'@@ -1,3 +1,2 @@\n',
1257
' how are you today?\n'
1259
, list(unified_diff(txt_a, txt_b,
1260
fromfile='a', tofile='b',
1261
fromfiledate='2008-08-08',
1262
tofiledate='2008-09-09',
1263
sequencematcher=psm)))
1266
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1268
_test_needs_features = [features.compiled_patiencediff_feature]
1271
super(TestPatienceDiffLib_c, self).setUp()
1272
from breezy import _patiencediff_c
1273
self._unique_lcs = _patiencediff_c.unique_lcs_c
1274
self._recurse_matches = _patiencediff_c.recurse_matches_c
1275
self._PatienceSequenceMatcher = \
1276
_patiencediff_c.PatienceSequenceMatcher_c
1278
def test_unhashable(self):
1279
"""We should get a proper exception here."""
1280
# We need to be able to hash items in the sequence, lists are
1281
# unhashable, and thus cannot be diffed
1282
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1284
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1285
None, ['valid', []], [])
1286
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1287
None, ['valid'], [[]])
1288
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1289
None, ['valid'], ['valid', []])
1292
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1295
super(TestPatienceDiffLibFiles, self).setUp()
1296
self._PatienceSequenceMatcher = \
1297
_patiencediff_py.PatienceSequenceMatcher_py
1299
def test_patience_unified_diff_files(self):
1300
txt_a = [b'hello there\n',
1302
b'how are you today?\n']
1303
txt_b = [b'hello there\n',
1304
b'how are you today?\n']
1305
with open('a1', 'wb') as f: f.writelines(txt_a)
1306
with open('b1', 'wb') as f: f.writelines(txt_b)
1308
unified_diff_files = patiencediff.unified_diff_files
1309
psm = self._PatienceSequenceMatcher
1310
self.assertEqual([b'--- a1\n',
1312
b'@@ -1,3 +1,2 @@\n',
1315
b' how are you today?\n',
1317
, list(unified_diff_files(b'a1', b'b1',
1318
sequencematcher=psm)))
1320
txt_a = [x+'\n' for x in 'abcdefghijklmnop']
1321
txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
1322
with open('a2', 'wt') as f: f.writelines(txt_a)
1323
with open('b2', 'wt') as f: f.writelines(txt_b)
1325
# This is the result with LongestCommonSubstring matching
1326
self.assertEqual([b'--- a2\n',
1328
b'@@ -1,6 +1,11 @@\n',
1340
, list(unified_diff_files(b'a2', b'b2')))
1342
# And the patience diff
1343
self.assertEqual([b'--- a2\n',
1345
b'@@ -4,6 +4,11 @@\n',
1357
list(unified_diff_files(b'a2', b'b2',
1358
sequencematcher=psm)))
1361
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1363
_test_needs_features = [features.compiled_patiencediff_feature]
1366
super(TestPatienceDiffLibFiles_c, self).setUp()
1367
from breezy import _patiencediff_c
1368
self._PatienceSequenceMatcher = \
1369
_patiencediff_c.PatienceSequenceMatcher_c
1372
class TestUsingCompiledIfAvailable(tests.TestCase):
1374
def test_PatienceSequenceMatcher(self):
1375
if features.compiled_patiencediff_feature.available():
1376
from breezy._patiencediff_c import PatienceSequenceMatcher_c
1377
self.assertIs(PatienceSequenceMatcher_c,
1378
patiencediff.PatienceSequenceMatcher)
1380
from breezy._patiencediff_py import PatienceSequenceMatcher_py
1381
self.assertIs(PatienceSequenceMatcher_py,
1382
patiencediff.PatienceSequenceMatcher)
1384
def test_unique_lcs(self):
1385
if features.compiled_patiencediff_feature.available():
1386
from breezy._patiencediff_c import unique_lcs_c
1387
self.assertIs(unique_lcs_c,
1388
patiencediff.unique_lcs)
1390
from breezy._patiencediff_py import unique_lcs_py
1391
self.assertIs(unique_lcs_py,
1392
patiencediff.unique_lcs)
1394
def test_recurse_matches(self):
1395
if features.compiled_patiencediff_feature.available():
1396
from breezy._patiencediff_c import recurse_matches_c
1397
self.assertIs(recurse_matches_c,
1398
patiencediff.recurse_matches)
1400
from breezy._patiencediff_py import recurse_matches_py
1401
self.assertIs(recurse_matches_py,
1402
patiencediff.recurse_matches)
865
b'.*a-file(.|\n)*b-file')
1405
868
class TestDiffFromTool(tests.TestCaseWithTransport):