139
139
lines = udiff_lines([b'boo\n'], [b'boo'])
140
140
self.check_patch(lines)
141
141
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
142
## "expected no-nl, got %r" % lines[5]
142
## "expected no-nl, got %r" % lines[5]
144
144
def check_patch(self, lines):
145
145
self.assertTrue(len(lines) > 1)
146
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
147
self.assertTrue(lines[0].startswith (b'---'))
148
## 'No orig line for patch:\n%s' % "".join(lines)
149
self.assertTrue(lines[1].startswith (b'+++'))
150
## 'No mod line for patch:\n%s' % "".join(lines)
146
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
147
self.assertTrue(lines[0].startswith(b'---'))
148
## 'No orig line for patch:\n%s' % "".join(lines)
149
self.assertTrue(lines[1].startswith(b'+++'))
150
## 'No mod line for patch:\n%s' % "".join(lines)
151
151
self.assertTrue(len(lines) > 2)
152
## "No hunks for patch:\n%s" % "".join(lines)
152
## "No hunks for patch:\n%s" % "".join(lines)
153
153
self.assertTrue(lines[2].startswith(b'@@'))
154
## "No hunk header for patch:\n%s" % "".join(lines)
154
## "No hunk header for patch:\n%s" % "".join(lines)
155
155
self.assertTrue(b'@@' in lines[2][2:])
156
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
156
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
158
158
def test_binary_lines(self):
263
261
def test_internal_diff_default_context(self):
264
262
output = BytesIO()
265
263
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
266
b'same_text\n', b'same_text\n', b'old_text\n'],
264
b'same_text\n', b'same_text\n', b'old_text\n'],
267
265
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
268
b'same_text\n', b'same_text\n', b'new_text\n'], output)
266
b'same_text\n', b'same_text\n', b'new_text\n'], output)
269
267
lines = output.getvalue().splitlines(True)
270
268
self.check_patch(lines)
271
269
self.assertEqual([b'--- old\n',
273
b'@@ -3,4 +3,4 @@\n',
271
b'@@ -3,4 +3,4 @@\n',
283
280
def test_internal_diff_no_context(self):
284
281
output = BytesIO()
285
282
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
286
b'same_text\n', b'same_text\n', b'old_text\n'],
283
b'same_text\n', b'same_text\n', b'old_text\n'],
287
284
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
288
b'same_text\n', b'same_text\n', b'new_text\n'], output,
285
b'same_text\n', b'same_text\n', b'new_text\n'], output,
290
287
lines = output.getvalue().splitlines(True)
291
288
self.check_patch(lines)
292
289
self.assertEqual([b'--- old\n',
294
b'@@ -6,1 +6,1 @@\n',
291
b'@@ -6,1 +6,1 @@\n',
301
297
def test_internal_diff_more_context(self):
302
298
output = BytesIO()
303
299
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
304
b'same_text\n', b'same_text\n', b'old_text\n'],
300
b'same_text\n', b'same_text\n', b'old_text\n'],
305
301
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
306
b'same_text\n', b'same_text\n', b'new_text\n'], output,
302
b'same_text\n', b'same_text\n', b'new_text\n'], output,
308
304
lines = output.getvalue().splitlines(True)
309
305
self.check_patch(lines)
332
326
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
334
328
cmd = ['diff', '-u', '--binary', 'old', 'new']
335
with open('old', 'wb') as f: f.write(b'\x00foobar\n')
336
with open('new', 'wb') as f: f.write(b'foo\x00bar\n')
329
with open('old', 'wb') as f:
330
f.write(b'\x00foobar\n')
331
with open('new', 'wb') as f:
332
f.write(b'foo\x00bar\n')
337
333
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
338
stdin=subprocess.PIPE)
334
stdin=subprocess.PIPE)
339
335
out, err = pipe.communicate()
340
336
# We should output whatever diff tells us, plus a trailing newline
341
337
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
367
363
self.wt.add(['file1', 'file2'])
369
365
message='Revision 1',
370
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
366
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
373
369
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
375
371
message='Revision 2',
376
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
372
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
379
375
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
381
377
message='Revision 3',
382
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
378
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
385
381
self.wt.remove(['file2'])
387
383
message='Revision 4',
388
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
384
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
391
387
self.build_tree_contents([
392
388
('file1', b'file1 contents in working tree\n')
394
390
# set the date stamps for files in the working tree to known values
395
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
391
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
397
393
def test_diff_rev_tree_working_tree(self):
398
394
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
636
631
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
638
633
tree = self.make_branch_and_tree('tree')
639
self.build_tree_contents([('tree/ren_'+alpha, b'contents\n')])
640
tree.add(['ren_'+alpha], [b'file-id-2'])
641
self.build_tree_contents([('tree/del_'+alpha, b'contents\n')])
642
tree.add(['del_'+alpha], [b'file-id-3'])
643
self.build_tree_contents([('tree/mod_'+alpha, b'contents\n')])
644
tree.add(['mod_'+alpha], [b'file-id-4'])
634
self.build_tree_contents([('tree/ren_' + alpha, b'contents\n')])
635
tree.add(['ren_' + alpha], [b'file-id-2'])
636
self.build_tree_contents([('tree/del_' + alpha, b'contents\n')])
637
tree.add(['del_' + alpha], [b'file-id-3'])
638
self.build_tree_contents([('tree/mod_' + alpha, b'contents\n')])
639
tree.add(['mod_' + alpha], [b'file-id-4'])
646
641
tree.commit('one', rev_id=b'rev-1')
648
tree.rename_one('ren_'+alpha, 'ren_'+omega)
649
tree.remove('del_'+alpha)
650
self.build_tree_contents([('tree/add_'+alpha, b'contents\n')])
651
tree.add(['add_'+alpha], [b'file-id'])
652
self.build_tree_contents([('tree/mod_'+alpha, b'contents_mod\n')])
643
tree.rename_one('ren_' + alpha, 'ren_' + omega)
644
tree.remove('del_' + alpha)
645
self.build_tree_contents([('tree/add_' + alpha, b'contents\n')])
646
tree.add(['add_' + alpha], [b'file-id'])
647
self.build_tree_contents([('tree/mod_' + alpha, b'contents_mod\n')])
654
649
d = get_diff_as_string(tree.basis_tree(), tree)
655
650
self.assertContainsRe(d,
656
b"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
657
self.assertContainsRe(d, b"=== added file 'add_%s'"%autf8)
658
self.assertContainsRe(d, b"=== modified file 'mod_%s'"%autf8)
659
self.assertContainsRe(d, b"=== removed file 'del_%s'"%autf8)
651
b"=== renamed file 'ren_%s' => 'ren_%s'\n" % (autf8, outf8))
652
self.assertContainsRe(d, b"=== added file 'add_%s'" % autf8)
653
self.assertContainsRe(d, b"=== modified file 'mod_%s'" % autf8)
654
self.assertContainsRe(d, b"=== removed file 'del_%s'" % autf8)
661
656
def test_unicode_filename_path_encoding(self):
662
657
"""Test for bug #382699: unicode filenames on Windows should be shown
897
892
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
898
893
self.assertEqual(unique_lcs('a', 'b'), [])
899
894
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)])
895
self.assertEqual(unique_lcs('abcde', 'cdeab'),
896
[(2, 0), (3, 1), (4, 2)])
897
self.assertEqual(unique_lcs('cdeab', 'abcde'),
898
[(0, 2), (1, 3), (2, 4)])
902
899
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
904
901
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
906
903
def test_recurse_matches(self):
968
965
# make sure it supports passing in lists
969
966
self.assertDiffBlocks(
972
'how are you today?\n'],
974
'how are you today?\n'],
975
[(0, 0, 1), (2, 1, 1)])
969
'how are you today?\n'],
971
'how are you today?\n'],
972
[(0, 0, 1), (2, 1, 1)])
977
974
# non unique lines surrounded by non-matching lines
979
976
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
981
978
# But they only need to be locally unique
982
self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
979
self.assertDiffBlocks('aBcDec', 'abcdec', [
980
(0, 0, 1), (2, 2, 1), (4, 4, 2)])
984
982
# non unique blocks won't be matched
985
983
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
987
985
# but locally unique ones will
988
986
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
989
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
987
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
991
989
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
992
990
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
1025
1023
chk_ops([], [], [])
1026
1024
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1027
1025
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),
1026
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1027
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1030
1028
('replace', 3, 4, 3, 4)
1032
1030
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1033
('equal', 1, 4, 0, 3),
1031
('equal', 1, 4, 0, 3),
1034
1032
('insert', 4, 4, 3, 4)
1036
1034
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),
1035
('equal', 1, 5, 0, 4)
1037
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1040
1038
('replace', 2, 3, 2, 3),
1041
('equal', 3, 5, 3, 5)
1043
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1039
('equal', 3, 5, 3, 5)
1041
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1044
1042
('replace', 2, 3, 2, 5),
1045
('equal', 3, 5, 5, 7)
1043
('equal', 3, 5, 5, 7)
1045
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1046
('insert', 2, 2, 2, 5),
1047
('equal', 2, 4, 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
1049
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1052
[('equal', 0, 6, 0, 6),
1053
('insert', 6, 6, 6, 11),
1054
('equal', 6, 16, 11, 21)
1050
[('equal', 0, 6, 0, 6),
1051
('insert', 6, 6, 6, 11),
1052
('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),
1055
['hello there\n', 'world\n', 'how are you today?\n'],
1056
['hello there\n', 'how are you today?\n'],
1057
[('equal', 0, 1, 0, 1),
1058
('delete', 1, 2, 1, 1),
1059
('equal', 2, 3, 1, 2),
1066
1061
chk_ops('aBccDe', 'abccde',
1067
[('equal', 0, 1, 0, 1),
1062
[('equal', 0, 1, 0, 1),
1068
1063
('replace', 1, 5, 1, 5),
1069
('equal', 5, 6, 5, 6),
1064
('equal', 5, 6, 5, 6),
1071
1066
chk_ops('aBcDec', 'abcdec',
1072
[('equal', 0, 1, 0, 1),
1067
[('equal', 0, 1, 0, 1),
1073
1068
('replace', 1, 2, 1, 2),
1074
('equal', 2, 3, 2, 3),
1069
('equal', 2, 3, 2, 3),
1075
1070
('replace', 3, 4, 3, 4),
1076
('equal', 4, 6, 4, 6),
1071
('equal', 4, 6, 4, 6),
1078
1073
chk_ops('aBcdEcdFg', 'abcdecdfg',
1079
[('equal', 0, 1, 0, 1),
1074
[('equal', 0, 1, 0, 1),
1080
1075
('replace', 1, 8, 1, 8),
1081
('equal', 8, 9, 8, 9)
1076
('equal', 8, 9, 8, 9)
1083
1078
chk_ops('aBcdEeXcdFg', 'abcdecdfg',
1084
[('equal', 0, 1, 0, 1),
1079
[('equal', 0, 1, 0, 1),
1085
1080
('replace', 1, 2, 1, 2),
1086
('equal', 2, 4, 2, 4),
1081
('equal', 2, 4, 2, 4),
1087
1082
('delete', 4, 5, 4, 4),
1088
('equal', 5, 6, 4, 5),
1083
('equal', 5, 6, 4, 5),
1089
1084
('delete', 6, 7, 5, 5),
1090
('equal', 7, 9, 5, 7),
1085
('equal', 7, 9, 5, 7),
1091
1086
('replace', 9, 10, 7, 8),
1092
('equal', 10, 11, 8, 9)
1087
('equal', 10, 11, 8, 9)
1095
1090
def test_grouped_opcodes(self):
1096
1091
def chk_ops(a, b, expected_codes, n=3):
1102
1097
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1103
1098
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
1104
1099
chk_ops('abcd', 'abcd', [])
1105
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1100
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1106
1101
('replace', 3, 4, 3, 4)
1108
1103
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1109
('equal', 1, 4, 0, 3),
1110
('insert', 4, 4, 3, 4)
1104
('equal', 1, 4, 0, 3),
1105
('insert', 4, 4, 3, 4)
1112
1107
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1113
[[('equal', 3, 6, 3, 6),
1108
[[('equal', 3, 6, 3, 6),
1114
1109
('insert', 6, 6, 6, 11),
1115
('equal', 6, 9, 11, 14)
1110
('equal', 6, 9, 11, 14)
1117
1112
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1118
[[('equal', 2, 6, 2, 6),
1113
[[('equal', 2, 6, 2, 6),
1119
1114
('insert', 6, 6, 6, 11),
1120
('equal', 6, 10, 11, 15)
1115
('equal', 6, 10, 11, 15)
1122
1117
chk_ops('Xabcdef', 'abcdef',
1123
1118
[[('delete', 0, 1, 0, 0),
1124
('equal', 1, 4, 0, 3)
1119
('equal', 1, 4, 0, 3)
1126
1121
chk_ops('abcdef', 'abcdefX',
1127
[[('equal', 3, 6, 3, 6),
1122
[[('equal', 3, 6, 3, 6),
1128
1123
('insert', 6, 6, 6, 7)
1132
1126
def test_multiple_ranges(self):
1133
1127
# There was an earlier bug where we used a bad set of ranges,
1134
1128
# this triggers that specific bug, to make sure it doesn't regress
1186
1180
pynff pzq_zxqve(Pbzznaq):
1187
'''.splitlines(True)
1188
, [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1181
'''.splitlines(True), [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1190
1183
def test_patience_unified_diff(self):
1191
1184
txt_a = ['hello there\n',
1196
1189
unified_diff = patiencediff.unified_diff
1197
1190
psm = self._PatienceSequenceMatcher
1198
1191
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']
1193
'@@ -1,3 +1,2 @@\n',
1196
' how are you today?\n'
1197
], list(unified_diff(txt_a, txt_b,
1198
sequencematcher=psm)))
1199
txt_a = [x + '\n' for x in 'abcdefghijklmnop']
1200
txt_b = [x + '\n' for x in 'abcdefxydefghijklmnop']
1209
1201
# This is the result with LongestCommonSubstring matching
1210
1202
self.assertEqual(['--- \n',
1212
'@@ -1,6 +1,11 @@\n',
1224
, list(unified_diff(txt_a, txt_b)))
1204
'@@ -1,6 +1,11 @@\n',
1215
' f\n'], list(unified_diff(txt_a, txt_b)))
1225
1216
# And the patience diff
1226
1217
self.assertEqual(['--- \n',
1228
'@@ -4,6 +4,11 @@\n',
1241
, list(unified_diff(txt_a, txt_b,
1242
sequencematcher=psm)))
1219
'@@ -4,6 +4,11 @@\n',
1231
], list(unified_diff(txt_a, txt_b,
1232
sequencematcher=psm)))
1244
1234
def test_patience_unified_diff_with_dates(self):
1245
1235
txt_a = ['hello there\n',
1250
1240
unified_diff = patiencediff.unified_diff
1251
1241
psm = self._PatienceSequenceMatcher
1252
1242
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)))
1243
'+++ b\t2008-09-09\n',
1244
'@@ -1,3 +1,2 @@\n',
1247
' how are you today?\n'
1248
], list(unified_diff(txt_a, txt_b,
1249
fromfile='a', tofile='b',
1250
fromfiledate='2008-08-08',
1251
tofiledate='2008-09-09',
1252
sequencematcher=psm)))
1266
1255
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1280
1269
# We need to be able to hash items in the sequence, lists are
1281
1270
# unhashable, and thus cannot be diffed
1282
1271
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', []])
1273
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1274
None, ['valid', []], [])
1275
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1276
None, ['valid'], [[]])
1277
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1278
None, ['valid'], ['valid', []])
1292
1281
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1313
1304
b' hello there\n',
1315
1306
b' how are you today?\n',
1317
, list(unified_diff_files(b'a1', b'b1',
1318
sequencematcher=psm)))
1307
], list(unified_diff_files(b'a1', b'b1',
1308
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)
1310
txt_a = [x + '\n' for x in 'abcdefghijklmnop']
1311
txt_b = [x + '\n' for x in 'abcdefxydefghijklmnop']
1312
with open('a2', 'wt') as f:
1314
with open('b2', 'wt') as f:
1325
1317
# This is the result with LongestCommonSubstring matching
1326
1318
self.assertEqual([b'--- a2\n',