138
139
lines = udiff_lines([b'boo\n'], [b'boo'])
139
140
self.check_patch(lines)
140
141
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
141
## "expected no-nl, got %r" % lines[5]
142
## "expected no-nl, got %r" % lines[5]
143
144
def check_patch(self, lines):
144
145
self.assertTrue(len(lines) > 1)
145
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
146
self.assertTrue(lines[0].startswith (b'---'))
147
## 'No orig line for patch:\n%s' % "".join(lines)
148
self.assertTrue(lines[1].startswith (b'+++'))
149
## '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)
150
151
self.assertTrue(len(lines) > 2)
151
## "No hunks for patch:\n%s" % "".join(lines)
152
## "No hunks for patch:\n%s" % "".join(lines)
152
153
self.assertTrue(lines[2].startswith(b'@@'))
153
## "No hunk header for patch:\n%s" % "".join(lines)
154
## "No hunk header for patch:\n%s" % "".join(lines)
154
155
self.assertTrue(b'@@' in lines[2][2:])
155
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
156
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
157
158
def test_binary_lines(self):
262
261
def test_internal_diff_default_context(self):
263
262
output = BytesIO()
264
263
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
265
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'],
266
265
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
267
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)
268
267
lines = output.getvalue().splitlines(True)
269
268
self.check_patch(lines)
270
269
self.assertEqual([b'--- old\n',
272
b'@@ -3,4 +3,4 @@\n',
271
b'@@ -3,4 +3,4 @@\n',
282
280
def test_internal_diff_no_context(self):
283
281
output = BytesIO()
284
282
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
285
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'],
286
284
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
287
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,
289
287
lines = output.getvalue().splitlines(True)
290
288
self.check_patch(lines)
291
289
self.assertEqual([b'--- old\n',
293
b'@@ -6,1 +6,1 @@\n',
291
b'@@ -6,1 +6,1 @@\n',
300
297
def test_internal_diff_more_context(self):
301
298
output = BytesIO()
302
299
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
303
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'],
304
301
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
305
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,
307
304
lines = output.getvalue().splitlines(True)
308
305
self.check_patch(lines)
331
326
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
333
328
cmd = ['diff', '-u', '--binary', 'old', 'new']
334
with open('old', 'wb') as f: f.write(b'\x00foobar\n')
335
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')
336
333
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
337
stdin=subprocess.PIPE)
334
stdin=subprocess.PIPE)
338
335
out, err = pipe.communicate()
339
336
# We should output whatever diff tells us, plus a trailing newline
340
337
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
366
363
self.wt.add(['file1', 'file2'])
368
365
message='Revision 1',
369
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
366
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
372
369
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
374
371
message='Revision 2',
375
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
372
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
378
375
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
380
377
message='Revision 3',
381
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
378
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
384
381
self.wt.remove(['file2'])
386
383
message='Revision 4',
387
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
384
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
390
387
self.build_tree_contents([
391
388
('file1', b'file1 contents in working tree\n')
393
390
# set the date stamps for files in the working tree to known values
394
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
396
393
def test_diff_rev_tree_working_tree(self):
397
394
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
635
631
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
637
633
tree = self.make_branch_and_tree('tree')
638
self.build_tree_contents([('tree/ren_'+alpha, b'contents\n')])
639
tree.add(['ren_'+alpha], [b'file-id-2'])
640
self.build_tree_contents([('tree/del_'+alpha, b'contents\n')])
641
tree.add(['del_'+alpha], [b'file-id-3'])
642
self.build_tree_contents([('tree/mod_'+alpha, b'contents\n')])
643
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'])
645
641
tree.commit('one', rev_id=b'rev-1')
647
tree.rename_one('ren_'+alpha, 'ren_'+omega)
648
tree.remove('del_'+alpha)
649
self.build_tree_contents([('tree/add_'+alpha, b'contents\n')])
650
tree.add(['add_'+alpha], [b'file-id'])
651
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')])
653
649
d = get_diff_as_string(tree.basis_tree(), tree)
654
650
self.assertContainsRe(d,
655
b"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
656
self.assertContainsRe(d, b"=== added file 'add_%s'"%autf8)
657
self.assertContainsRe(d, b"=== modified file 'mod_%s'"%autf8)
658
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)
660
656
def test_unicode_filename_path_encoding(self):
661
657
"""Test for bug #382699: unicode filenames on Windows should be shown
896
892
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
897
893
self.assertEqual(unique_lcs('a', 'b'), [])
898
894
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
899
self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
900
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)])
901
899
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
903
901
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
905
903
def test_recurse_matches(self):
967
965
# make sure it supports passing in lists
968
966
self.assertDiffBlocks(
971
'how are you today?\n'],
973
'how are you today?\n'],
974
[(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)])
976
974
# non unique lines surrounded by non-matching lines
978
976
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
980
978
# But they only need to be locally unique
981
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)])
983
982
# non unique blocks won't be matched
984
983
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
986
985
# but locally unique ones will
987
986
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
988
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
987
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
990
989
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
991
990
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
1024
1023
chk_ops([], [], [])
1025
1024
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1026
1025
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1027
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1028
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),
1029
1028
('replace', 3, 4, 3, 4)
1031
1030
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1032
('equal', 1, 4, 0, 3),
1031
('equal', 1, 4, 0, 3),
1033
1032
('insert', 4, 4, 3, 4)
1035
1034
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1036
('equal', 1, 5, 0, 4)
1038
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),
1039
1038
('replace', 2, 3, 2, 3),
1040
('equal', 3, 5, 3, 5)
1042
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),
1043
1042
('replace', 2, 3, 2, 5),
1044
('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)
1046
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1047
('insert', 2, 2, 2, 5),
1048
('equal', 2, 4, 5, 7)
1050
1049
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1051
[('equal', 0, 6, 0, 6),
1052
('insert', 6, 6, 6, 11),
1053
('equal', 6, 16, 11, 21)
1050
[('equal', 0, 6, 0, 6),
1051
('insert', 6, 6, 6, 11),
1052
('equal', 6, 16, 11, 21)
1058
, 'how are you today?\n'],
1060
, 'how are you today?\n'],
1061
[('equal', 0, 1, 0, 1),
1062
('delete', 1, 2, 1, 1),
1063
('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),
1065
1061
chk_ops('aBccDe', 'abccde',
1066
[('equal', 0, 1, 0, 1),
1062
[('equal', 0, 1, 0, 1),
1067
1063
('replace', 1, 5, 1, 5),
1068
('equal', 5, 6, 5, 6),
1064
('equal', 5, 6, 5, 6),
1070
1066
chk_ops('aBcDec', 'abcdec',
1071
[('equal', 0, 1, 0, 1),
1067
[('equal', 0, 1, 0, 1),
1072
1068
('replace', 1, 2, 1, 2),
1073
('equal', 2, 3, 2, 3),
1069
('equal', 2, 3, 2, 3),
1074
1070
('replace', 3, 4, 3, 4),
1075
('equal', 4, 6, 4, 6),
1071
('equal', 4, 6, 4, 6),
1077
1073
chk_ops('aBcdEcdFg', 'abcdecdfg',
1078
[('equal', 0, 1, 0, 1),
1074
[('equal', 0, 1, 0, 1),
1079
1075
('replace', 1, 8, 1, 8),
1080
('equal', 8, 9, 8, 9)
1076
('equal', 8, 9, 8, 9)
1082
1078
chk_ops('aBcdEeXcdFg', 'abcdecdfg',
1083
[('equal', 0, 1, 0, 1),
1079
[('equal', 0, 1, 0, 1),
1084
1080
('replace', 1, 2, 1, 2),
1085
('equal', 2, 4, 2, 4),
1081
('equal', 2, 4, 2, 4),
1086
1082
('delete', 4, 5, 4, 4),
1087
('equal', 5, 6, 4, 5),
1083
('equal', 5, 6, 4, 5),
1088
1084
('delete', 6, 7, 5, 5),
1089
('equal', 7, 9, 5, 7),
1085
('equal', 7, 9, 5, 7),
1090
1086
('replace', 9, 10, 7, 8),
1091
('equal', 10, 11, 8, 9)
1087
('equal', 10, 11, 8, 9)
1094
1090
def test_grouped_opcodes(self):
1095
1091
def chk_ops(a, b, expected_codes, n=3):
1101
1097
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1102
1098
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
1103
1099
chk_ops('abcd', 'abcd', [])
1104
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1100
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1105
1101
('replace', 3, 4, 3, 4)
1107
1103
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1108
('equal', 1, 4, 0, 3),
1109
('insert', 4, 4, 3, 4)
1104
('equal', 1, 4, 0, 3),
1105
('insert', 4, 4, 3, 4)
1111
1107
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1112
[[('equal', 3, 6, 3, 6),
1108
[[('equal', 3, 6, 3, 6),
1113
1109
('insert', 6, 6, 6, 11),
1114
('equal', 6, 9, 11, 14)
1110
('equal', 6, 9, 11, 14)
1116
1112
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1117
[[('equal', 2, 6, 2, 6),
1113
[[('equal', 2, 6, 2, 6),
1118
1114
('insert', 6, 6, 6, 11),
1119
('equal', 6, 10, 11, 15)
1115
('equal', 6, 10, 11, 15)
1121
1117
chk_ops('Xabcdef', 'abcdef',
1122
1118
[[('delete', 0, 1, 0, 0),
1123
('equal', 1, 4, 0, 3)
1119
('equal', 1, 4, 0, 3)
1125
1121
chk_ops('abcdef', 'abcdefX',
1126
[[('equal', 3, 6, 3, 6),
1122
[[('equal', 3, 6, 3, 6),
1127
1123
('insert', 6, 6, 6, 7)
1131
1126
def test_multiple_ranges(self):
1132
1127
# There was an earlier bug where we used a bad set of ranges,
1133
1128
# this triggers that specific bug, to make sure it doesn't regress
1185
1180
pynff pzq_zxqve(Pbzznaq):
1186
'''.splitlines(True)
1187
, [(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)])
1189
1183
def test_patience_unified_diff(self):
1190
1184
txt_a = ['hello there\n',
1195
1189
unified_diff = patiencediff.unified_diff
1196
1190
psm = self._PatienceSequenceMatcher
1197
1191
self.assertEqual(['--- \n',
1199
'@@ -1,3 +1,2 @@\n',
1202
' how are you today?\n'
1204
, list(unified_diff(txt_a, txt_b,
1205
sequencematcher=psm)))
1206
txt_a = [x+'\n' for x in 'abcdefghijklmnop']
1207
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']
1208
1201
# This is the result with LongestCommonSubstring matching
1209
1202
self.assertEqual(['--- \n',
1211
'@@ -1,6 +1,11 @@\n',
1223
, list(unified_diff(txt_a, txt_b)))
1204
'@@ -1,6 +1,11 @@\n',
1215
' f\n'], list(unified_diff(txt_a, txt_b)))
1224
1216
# And the patience diff
1225
1217
self.assertEqual(['--- \n',
1227
'@@ -4,6 +4,11 @@\n',
1240
, list(unified_diff(txt_a, txt_b,
1241
sequencematcher=psm)))
1219
'@@ -4,6 +4,11 @@\n',
1231
], list(unified_diff(txt_a, txt_b,
1232
sequencematcher=psm)))
1243
1234
def test_patience_unified_diff_with_dates(self):
1244
1235
txt_a = ['hello there\n',
1249
1240
unified_diff = patiencediff.unified_diff
1250
1241
psm = self._PatienceSequenceMatcher
1251
1242
self.assertEqual(['--- a\t2008-08-08\n',
1252
'+++ b\t2008-09-09\n',
1253
'@@ -1,3 +1,2 @@\n',
1256
' how are you today?\n'
1258
, list(unified_diff(txt_a, txt_b,
1259
fromfile='a', tofile='b',
1260
fromfiledate='2008-08-08',
1261
tofiledate='2008-09-09',
1262
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)))
1265
1255
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1279
1269
# We need to be able to hash items in the sequence, lists are
1280
1270
# unhashable, and thus cannot be diffed
1281
1271
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1283
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1284
None, ['valid', []], [])
1285
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1286
None, ['valid'], [[]])
1287
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1288
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', []])
1291
1281
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1312
1304
b' hello there\n',
1314
1306
b' how are you today?\n',
1316
, list(unified_diff_files(b'a1', b'b1',
1317
sequencematcher=psm)))
1307
], list(unified_diff_files(b'a1', b'b1',
1308
sequencematcher=psm)))
1319
txt_a = [x+'\n' for x in 'abcdefghijklmnop']
1320
txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
1321
with open('a2', 'wt') as f: f.writelines(txt_a)
1322
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:
1324
1317
# This is the result with LongestCommonSubstring matching
1325
1318
self.assertEqual([b'--- a2\n',
1494
1486
self.addCleanup(old_tree.unlock)
1495
1487
tree.lock_read()
1496
1488
self.addCleanup(tree.unlock)
1497
diff_obj = diff.DiffFromTool(['python', '-c',
1489
diff_obj = diff.DiffFromTool([sys.executable, '-c',
1498
1490
'print "@old_path @new_path"'],
1499
1491
old_tree, tree, output)
1500
1492
self.addCleanup(diff_obj.finish)
1501
1493
self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
1502
1494
old_path, new_path = diff_obj._prepare_files(
1503
'oldname', 'newname', file_id=b'file-id')
1495
'oldname', 'newname', file_id=b'file-id')
1504
1496
self.assertContainsRe(old_path, 'old/oldname$')
1505
1497
self.assertEqual(315532800, os.stat(old_path).st_mtime)
1506
1498
self.assertContainsRe(new_path, 'tree/newname$')