111
118
def test_add_nl(self):
112
119
"""diff generates a valid diff for patches that add a newline"""
113
lines = udiff_lines(['boo'], ['boo\n'])
120
lines = udiff_lines([b'boo'], [b'boo\n'])
114
121
self.check_patch(lines)
115
self.assertEqual(lines[4], '\\ No newline at end of file\n')
122
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
116
123
## "expected no-nl, got %r" % lines[4]
118
125
def test_add_nl_2(self):
119
126
"""diff generates a valid diff for patches that change last line and
122
lines = udiff_lines(['boo'], ['goo\n'])
129
lines = udiff_lines([b'boo'], [b'goo\n'])
123
130
self.check_patch(lines)
124
self.assertEqual(lines[4], '\\ No newline at end of file\n')
131
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
125
132
## "expected no-nl, got %r" % lines[4]
127
134
def test_remove_nl(self):
128
135
"""diff generates a valid diff for patches that change last line and
131
lines = udiff_lines(['boo\n'], ['boo'])
138
lines = udiff_lines([b'boo\n'], [b'boo'])
132
139
self.check_patch(lines)
133
self.assertEqual(lines[5], '\\ No newline at end of file\n')
140
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
134
141
## "expected no-nl, got %r" % lines[5]
136
143
def check_patch(self, lines):
137
144
self.assertTrue(len(lines) > 1)
138
145
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
139
self.assertTrue(lines[0].startswith ('---'))
146
self.assertTrue(lines[0].startswith (b'---'))
140
147
## 'No orig line for patch:\n%s' % "".join(lines)
141
self.assertTrue(lines[1].startswith ('+++'))
148
self.assertTrue(lines[1].startswith (b'+++'))
142
149
## 'No mod line for patch:\n%s' % "".join(lines)
143
150
self.assertTrue(len(lines) > 2)
144
151
## "No hunks for patch:\n%s" % "".join(lines)
145
self.assertTrue(lines[2].startswith('@@'))
152
self.assertTrue(lines[2].startswith(b'@@'))
146
153
## "No hunk header for patch:\n%s" % "".join(lines)
147
self.assertTrue('@@' in lines[2][2:])
154
self.assertTrue(b'@@' in lines[2][2:])
148
155
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
150
157
def test_binary_lines(self):
152
uni_lines = [1023 * 'a' + '\x00']
159
uni_lines = [1023 * b'a' + b'\x00']
153
160
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
154
161
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
155
162
udiff_lines(uni_lines, empty, allow_binary=True)
156
163
udiff_lines(empty, uni_lines, allow_binary=True)
158
165
def test_external_diff(self):
159
lines = external_udiff_lines(['boo\n'], ['goo\n'])
166
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
160
167
self.check_patch(lines)
161
self.assertEqual('\n', lines[-1])
168
self.assertEqual(b'\n', lines[-1])
163
170
def test_external_diff_no_fileno(self):
164
171
# Make sure that we can handle not having a fileno, even
165
172
# if the diff is large
166
lines = external_udiff_lines(['boo\n']*10000,
173
lines = external_udiff_lines([b'boo\n']*10000,
168
175
use_stringio=True)
169
176
self.check_patch(lines)
171
178
def test_external_diff_binary_lang_c(self):
172
179
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
173
180
self.overrideEnv(lang, 'C')
174
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
181
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
175
182
# Older versions of diffutils say "Binary files", newer
176
183
# versions just say "Files".
177
self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
178
self.assertEqual(lines[1:], ['\n'])
184
self.assertContainsRe(lines[0], b'(Binary f|F)iles old and new differ\n')
185
self.assertEqual(lines[1:], [b'\n'])
180
187
def test_no_external_diff(self):
181
188
"""Check that NoDiff is raised when diff is not available"""
183
190
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
184
191
self.overrideEnv('PATH', '')
185
192
self.assertRaises(errors.NoDiff, diff.external_diff,
186
'old', ['boo\n'], 'new', ['goo\n'],
193
b'old', [b'boo\n'], b'new', [b'goo\n'],
187
194
BytesIO(), diff_opts=['-u'])
189
196
def test_internal_diff_default(self):
190
197
# Default internal diff encoding is utf8
191
198
output = BytesIO()
192
diff.internal_diff(u'old_\xb5', ['old_text\n'],
193
u'new_\xe5', ['new_text\n'], output)
199
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
200
u'new_\xe5', [b'new_text\n'], output)
194
201
lines = output.getvalue().splitlines(True)
195
202
self.check_patch(lines)
196
self.assertEqual(['--- old_\xc2\xb5\n',
197
'+++ new_\xc3\xa5\n',
203
self.assertEqual([b'--- old_\xc2\xb5\n',
204
b'+++ new_\xc3\xa5\n',
205
b'@@ -1,1 +1,1 @@\n',
205
212
def test_internal_diff_utf8(self):
206
213
output = BytesIO()
207
diff.internal_diff(u'old_\xb5', ['old_text\n'],
208
u'new_\xe5', ['new_text\n'], output,
214
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
215
u'new_\xe5', [b'new_text\n'], output,
209
216
path_encoding='utf8')
210
217
lines = output.getvalue().splitlines(True)
211
218
self.check_patch(lines)
212
self.assertEqual(['--- old_\xc2\xb5\n',
213
'+++ new_\xc3\xa5\n',
219
self.assertEqual([b'--- old_\xc2\xb5\n',
220
b'+++ new_\xc3\xa5\n',
221
b'@@ -1,1 +1,1 @@\n',
249
256
def test_internal_diff_returns_bytes(self):
251
258
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
252
u'new_\xe5', [b'new_text\n'], output)
259
u'new_\xe5', [b'new_text\n'], output)
253
260
output.check_types(self, bytes)
255
262
def test_internal_diff_default_context(self):
256
263
output = BytesIO()
257
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
258
'same_text\n', 'same_text\n', 'old_text\n'],
259
'new', ['same_text\n', 'same_text\n', 'same_text\n',
260
'same_text\n', 'same_text\n', 'new_text\n'], output)
264
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'],
266
'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)
261
268
lines = output.getvalue().splitlines(True)
262
269
self.check_patch(lines)
263
self.assertEqual(['--- old\n',
270
self.assertEqual([b'--- old\n',
272
b'@@ -3,4 +3,4 @@\n',
275
282
def test_internal_diff_no_context(self):
276
283
output = BytesIO()
277
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
278
'same_text\n', 'same_text\n', 'old_text\n'],
279
'new', ['same_text\n', 'same_text\n', 'same_text\n',
280
'same_text\n', 'same_text\n', 'new_text\n'], output,
284
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'],
286
'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,
282
289
lines = output.getvalue().splitlines(True)
283
290
self.check_patch(lines)
284
self.assertEqual(['--- old\n',
291
self.assertEqual([b'--- old\n',
293
b'@@ -6,1 +6,1 @@\n',
293
300
def test_internal_diff_more_context(self):
294
301
output = BytesIO()
295
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
296
'same_text\n', 'same_text\n', 'old_text\n'],
297
'new', ['same_text\n', 'same_text\n', 'same_text\n',
298
'same_text\n', 'same_text\n', 'new_text\n'], output,
302
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'],
304
'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,
300
307
lines = output.getvalue().splitlines(True)
301
308
self.check_patch(lines)
302
self.assertEqual(['--- old\n',
309
self.assertEqual([b'--- old\n',
311
b'@@ -2,5 +2,5 @@\n',
488
495
self.build_tree_contents([('tree/file', b'new contents\n')])
489
496
d = get_diff_as_string(tree.basis_tree(), tree)
490
self.assertContainsRe(d, "=== modified file 'file'\n")
491
self.assertContainsRe(d, '--- old/file\t')
492
self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
493
self.assertContainsRe(d, '-contents\n'
497
self.assertContainsRe(d, b"=== modified file 'file'\n")
498
self.assertContainsRe(d, b'--- old/file\t')
499
self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
500
self.assertContainsRe(d, b'-contents\n'
501
b'\\+new contents\n')
496
503
def test_modified_file_in_renamed_dir(self):
497
504
"""Test when a file is modified in a renamed directory."""
504
511
tree.rename_one('dir', 'other')
505
512
self.build_tree_contents([('tree/other/file', b'new contents\n')])
506
513
d = get_diff_as_string(tree.basis_tree(), tree)
507
self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
508
self.assertContainsRe(d, "=== modified file 'other/file'\n")
514
self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
515
self.assertContainsRe(d, b"=== modified file 'other/file'\n")
509
516
# XXX: This is technically incorrect, because it used to be at another
510
517
# location. What to do?
511
self.assertContainsRe(d, '--- old/dir/file\t')
512
self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
513
self.assertContainsRe(d, '-contents\n'
518
self.assertContainsRe(d, b'--- old/dir/file\t')
519
self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
520
self.assertContainsRe(d, b'-contents\n'
521
b'\\+new contents\n')
516
523
def test_renamed_directory(self):
517
524
"""Test when only a directory is only renamed."""
551
558
tree.rename_one('file', 'newname')
552
559
self.build_tree_contents([('tree/newname', b'new contents\n')])
553
560
d = get_diff_as_string(tree.basis_tree(), tree)
554
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
555
self.assertContainsRe(d, '--- old/file\t')
556
self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
557
self.assertContainsRe(d, '-contents\n'
561
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
562
self.assertContainsRe(d, b'--- old/file\t')
563
self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
564
self.assertContainsRe(d, b'-contents\n'
565
b'\\+new contents\n')
561
568
def test_internal_diff_exec_property(self):
583
590
d = get_diff_as_string(tree.basis_tree(), tree)
585
self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
587
self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
589
self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
591
self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
593
self.assertNotContainsRe(d, r"file 'e'")
594
self.assertNotContainsRe(d, r"file 'f'")
592
self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
594
self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
596
self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
598
self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
600
self.assertNotContainsRe(d, br"file 'e'")
601
self.assertNotContainsRe(d, br"file 'f'")
596
603
def test_binary_unicode_filenames(self):
597
604
"""Test that contents of files are *not* encoded in UTF-8 when there
604
611
alpha, omega = u'\u03b1', u'\u03c9'
605
612
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
606
613
self.build_tree_contents(
607
[('tree/' + alpha, chr(0)),
614
[('tree/' + alpha, b'\0'),
608
615
('tree/' + omega,
609
('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
616
(b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
610
617
tree.add([alpha], [b'file-id'])
611
618
tree.add([omega], [b'file-id-2'])
612
619
diff_content = StubO()
613
620
diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
614
621
diff_content.check_types(self, bytes)
615
622
d = b''.join(diff_content.write_record)
616
self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
617
self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
623
self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
624
self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
618
625
% (alpha_utf8, alpha_utf8))
619
self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
620
self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
621
self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
626
self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
627
self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
628
self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
623
630
def test_unicode_filename(self):
624
631
"""Test when the filename are unicode."""
646
653
d = get_diff_as_string(tree.basis_tree(), tree)
647
654
self.assertContainsRe(d,
648
"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
649
self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
650
self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
651
self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
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)
653
660
def test_unicode_filename_path_encoding(self):
654
661
"""Test for bug #382699: unicode filenames on Windows should be shown
728
735
differ.diff_text('olddir/oldfile', None, 'old label',
729
736
'new label', b'file-id', None)
730
737
self.assertEqual(
731
'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
738
b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
732
739
differ.to_file.getvalue())
733
740
differ.to_file.seek(0)
734
741
differ.diff_text(None, 'newdir/newfile',
735
742
'old label', 'new label', None, b'file-id')
736
743
self.assertEqual(
737
'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
744
b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
738
745
differ.to_file.getvalue())
739
746
differ.to_file.seek(0)
740
747
differ.diff_text('olddir/oldfile', 'newdir/newfile',
741
748
'old label', 'new label', b'file-id', b'file-id')
742
749
self.assertEqual(
743
'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
750
b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
744
751
differ.to_file.getvalue())
746
753
def test_diff_deletion(self):
759
766
self.new_tree.add('file', b'file-id')
760
767
os.unlink('old-tree/file')
761
768
self.differ.show_diff(None)
762
self.assertContainsRe(self.differ.to_file.getvalue(), r'\+contents')
769
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
764
771
def test_diff_symlink(self):
765
772
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
766
773
differ.diff_symlink('old target', None)
767
self.assertEqual("=== target was 'old target'\n",
774
self.assertEqual(b"=== target was 'old target'\n",
768
775
differ.to_file.getvalue())
770
777
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
771
778
differ.diff_symlink(None, 'new target')
772
self.assertEqual("=== target is 'new target'\n",
779
self.assertEqual(b"=== target is 'new target'\n",
773
780
differ.to_file.getvalue())
775
782
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
776
783
differ.diff_symlink('old target', 'new target')
777
self.assertEqual("=== target changed 'old target' => 'new target'\n",
784
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
778
785
differ.to_file.getvalue())
780
787
def test_diff(self):
1300
1307
unified_diff_files = patiencediff.unified_diff_files
1301
1308
psm = self._PatienceSequenceMatcher
1302
self.assertEqual(['--- a1\n',
1304
'@@ -1,3 +1,2 @@\n',
1307
' how are you today?\n',
1309
self.assertEqual([b'--- a1\n',
1311
b'@@ -1,3 +1,2 @@\n',
1314
b' how are you today?\n',
1309
, list(unified_diff_files('a1', 'b1',
1316
, list(unified_diff_files(b'a1', b'b1',
1310
1317
sequencematcher=psm)))
1312
1319
txt_a = [x+'\n' for x in 'abcdefghijklmnop']
1313
1320
txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
1314
with open('a2', 'wb') as f: f.writelines(txt_a)
1315
with open('b2', 'wb') as f: f.writelines(txt_b)
1321
with open('a2', 'wt') as f: f.writelines(txt_a)
1322
with open('b2', 'wt') as f: f.writelines(txt_b)
1317
1324
# This is the result with LongestCommonSubstring matching
1318
self.assertEqual(['--- a2\n',
1320
'@@ -1,6 +1,11 @@\n',
1332
, list(unified_diff_files('a2', 'b2')))
1325
self.assertEqual([b'--- a2\n',
1327
b'@@ -1,6 +1,11 @@\n',
1339
, list(unified_diff_files(b'a2', b'b2')))
1334
1341
# And the patience diff
1335
self.assertEqual(['--- a2\n',
1337
'@@ -4,6 +4,11 @@\n',
1349
list(unified_diff_files('a2', 'b2',
1342
self.assertEqual([b'--- a2\n',
1344
b'@@ -4,6 +4,11 @@\n',
1356
list(unified_diff_files(b'a2', b'b2',
1350
1357
sequencematcher=psm)))