79
"""Simple file-like object that allows writes with any type and records."""
82
self.write_record = []
84
def write(self, data):
85
self.write_record.append(data)
87
def check_types(self, testcase, expected_type):
89
any(not isinstance(o, expected_type) for o in self.write_record),
90
"Not all writes of type %s: %r" % (
91
expected_type.__name__, self.write_record))
94
class TestDiffOptions(tests.TestCase):
96
def test_unified_added(self):
97
"""Check for default style '-u' only if no other style specified
100
# Verify that style defaults to unified, id est '-u' appended
101
# to option list, in the absence of an alternative style.
102
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
105
class TestDiffOptionsScenarios(tests.TestCase):
107
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
108
style = None # Set by load_tests_apply_scenarios from scenarios
110
def test_unified_not_added(self):
111
# Verify that for all valid style options, '-u' is not
112
# appended to option list.
113
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
114
self.assertEqual(["%s" % (self.style,)], ret_opts)
83
117
class TestDiff(tests.TestCase):
85
119
def test_add_nl(self):
86
120
"""diff generates a valid diff for patches that add a newline"""
87
lines = udiff_lines(['boo'], ['boo\n'])
121
lines = udiff_lines([b'boo'], [b'boo\n'])
88
122
self.check_patch(lines)
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
90
## "expected no-nl, got %r" % lines[4]
123
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
124
## "expected no-nl, got %r" % lines[4]
92
126
def test_add_nl_2(self):
93
127
"""diff generates a valid diff for patches that change last line and
96
lines = udiff_lines(['boo'], ['goo\n'])
130
lines = udiff_lines([b'boo'], [b'goo\n'])
97
131
self.check_patch(lines)
98
self.assertEquals(lines[4], '\\ No newline at end of file\n')
99
## "expected no-nl, got %r" % lines[4]
132
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
133
## "expected no-nl, got %r" % lines[4]
101
135
def test_remove_nl(self):
102
136
"""diff generates a valid diff for patches that change last line and
105
lines = udiff_lines(['boo\n'], ['boo'])
139
lines = udiff_lines([b'boo\n'], [b'boo'])
106
140
self.check_patch(lines)
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
108
## "expected no-nl, got %r" % lines[5]
141
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
142
## "expected no-nl, got %r" % lines[5]
110
144
def check_patch(self, lines):
111
self.assert_(len(lines) > 1)
112
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
113
self.assert_(lines[0].startswith ('---'))
114
## 'No orig line for patch:\n%s' % "".join(lines)
115
self.assert_(lines[1].startswith ('+++'))
116
## 'No mod line for patch:\n%s' % "".join(lines)
117
self.assert_(len(lines) > 2)
118
## "No hunks for patch:\n%s" % "".join(lines)
119
self.assert_(lines[2].startswith('@@'))
120
## "No hunk header for patch:\n%s" % "".join(lines)
121
self.assert_('@@' in lines[2][2:])
122
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
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)
151
self.assertTrue(len(lines) > 2)
152
## "No hunks for patch:\n%s" % "".join(lines)
153
self.assertTrue(lines[2].startswith(b'@@'))
154
## "No hunk header for patch:\n%s" % "".join(lines)
155
self.assertTrue(b'@@' in lines[2][2:])
156
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
158
def test_binary_lines(self):
126
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
160
uni_lines = [1023 * b'a' + b'\x00']
161
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
162
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
udiff_lines(uni_lines , empty, allow_binary=True)
163
udiff_lines(uni_lines, empty, allow_binary=True)
130
164
udiff_lines(empty, uni_lines, allow_binary=True)
132
166
def test_external_diff(self):
133
lines = external_udiff_lines(['boo\n'], ['goo\n'])
167
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
134
168
self.check_patch(lines)
135
self.assertEqual('\n', lines[-1])
169
self.assertEqual(b'\n', lines[-1])
137
171
def test_external_diff_no_fileno(self):
138
172
# Make sure that we can handle not having a fileno, even
139
173
# if the diff is large
140
lines = external_udiff_lines(['boo\n']*10000,
174
lines = external_udiff_lines([b'boo\n'] * 10000,
142
176
use_stringio=True)
143
177
self.check_patch(lines)
145
179
def test_external_diff_binary_lang_c(self):
147
180
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
old_env[lang] = osutils.set_or_unset_env(lang, 'C')
150
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
151
# Older versions of diffutils say "Binary files", newer
152
# versions just say "Files".
153
self.assertContainsRe(lines[0],
154
'(Binary f|F)iles old and new differ\n')
155
self.assertEquals(lines[1:], ['\n'])
157
for lang, old_val in old_env.iteritems():
158
osutils.set_or_unset_env(lang, old_val)
181
self.overrideEnv(lang, 'C')
182
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
183
# Older versions of diffutils say "Binary files", newer
184
# versions just say "Files".
185
self.assertContainsRe(
186
lines[0], b'(Binary f|F)iles old and new differ\n')
187
self.assertEqual(lines[1:], [b'\n'])
160
189
def test_no_external_diff(self):
161
190
"""Check that NoDiff is raised when diff is not available"""
162
# Use os.environ['PATH'] to make sure no 'diff' command is available
163
orig_path = os.environ['PATH']
165
os.environ['PATH'] = ''
166
self.assertRaises(errors.NoDiff, diff.external_diff,
167
'old', ['boo\n'], 'new', ['goo\n'],
168
StringIO(), diff_opts=['-u'])
170
os.environ['PATH'] = orig_path
191
# Make sure no 'diff' command is available
192
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
193
self.overrideEnv('PATH', '')
194
self.assertRaises(errors.NoDiff, diff.external_diff,
195
b'old', [b'boo\n'], b'new', [b'goo\n'],
196
BytesIO(), diff_opts=['-u'])
172
198
def test_internal_diff_default(self):
173
199
# Default internal diff encoding is utf8
175
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
u'new_\xe5', ['new_text\n'], output)
201
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
202
u'new_\xe5', [b'new_text\n'], output)
177
203
lines = output.getvalue().splitlines(True)
178
204
self.check_patch(lines)
179
self.assertEquals(['--- old_\xc2\xb5\n',
180
'+++ new_\xc3\xa5\n',
205
self.assertEqual([b'--- old_\xc2\xb5\n',
206
b'+++ new_\xc3\xa5\n',
207
b'@@ -1,1 +1,1 @@\n',
188
213
def test_internal_diff_utf8(self):
190
diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
u'new_\xe5', ['new_text\n'], output,
215
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
216
u'new_\xe5', [b'new_text\n'], output,
192
217
path_encoding='utf8')
193
218
lines = output.getvalue().splitlines(True)
194
219
self.check_patch(lines)
195
self.assertEquals(['--- old_\xc2\xb5\n',
196
'+++ new_\xc3\xa5\n',
220
self.assertEqual([b'--- old_\xc2\xb5\n',
221
b'+++ new_\xc3\xa5\n',
222
b'@@ -1,1 +1,1 @@\n',
204
228
def test_internal_diff_iso_8859_1(self):
206
diff.internal_diff(u'old_\xb5', ['old_text\n'],
207
u'new_\xe5', ['new_text\n'], output,
230
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
231
u'new_\xe5', [b'new_text\n'], output,
208
232
path_encoding='iso-8859-1')
209
233
lines = output.getvalue().splitlines(True)
210
234
self.check_patch(lines)
211
self.assertEquals(['--- old_\xb5\n',
235
self.assertEqual([b'--- old_\xb5\n',
237
b'@@ -1,1 +1,1 @@\n',
220
243
def test_internal_diff_no_content(self):
222
245
diff.internal_diff(u'old', [], u'new', [], output)
223
self.assertEqual('', output.getvalue())
246
self.assertEqual(b'', output.getvalue())
225
248
def test_internal_diff_no_changes(self):
227
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
u'new', ['text\n', 'contents\n'],
250
diff.internal_diff(u'old', [b'text\n', b'contents\n'],
251
u'new', [b'text\n', b'contents\n'],
230
self.assertEqual('', output.getvalue())
253
self.assertEqual(b'', output.getvalue())
232
255
def test_internal_diff_returns_bytes(self):
234
output = StringIO.StringIO()
235
diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
u'new_\xe5', ['new_text\n'], output)
237
self.failUnless(isinstance(output.getvalue(), str),
238
'internal_diff should return bytestrings')
257
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
258
u'new_\xe5', [b'new_text\n'], output)
259
output.check_types(self, bytes)
261
def test_internal_diff_default_context(self):
263
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
264
b'same_text\n', b'same_text\n', b'old_text\n'],
265
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
266
b'same_text\n', b'same_text\n', b'new_text\n'], output)
267
lines = output.getvalue().splitlines(True)
268
self.check_patch(lines)
269
self.assertEqual([b'--- old\n',
271
b'@@ -3,4 +3,4 @@\n',
280
def test_internal_diff_no_context(self):
282
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
283
b'same_text\n', b'same_text\n', b'old_text\n'],
284
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
285
b'same_text\n', b'same_text\n', b'new_text\n'], output,
287
lines = output.getvalue().splitlines(True)
288
self.check_patch(lines)
289
self.assertEqual([b'--- old\n',
291
b'@@ -6,1 +6,1 @@\n',
297
def test_internal_diff_more_context(self):
299
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
300
b'same_text\n', b'same_text\n', b'old_text\n'],
301
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
302
b'same_text\n', b'same_text\n', b'new_text\n'], output,
304
lines = output.getvalue().splitlines(True)
305
self.check_patch(lines)
306
self.assertEqual([b'--- old\n',
308
b'@@ -2,5 +2,5 @@\n',
241
319
class TestDiffFiles(tests.TestCaseInTempDir):
243
321
def test_external_diff_binary(self):
244
322
"""The output when using external diff should use diff's i18n error"""
323
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
324
self.overrideEnv(lang, 'C')
245
325
# Make sure external_diff doesn't fail in the current LANG
246
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
326
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
248
328
cmd = ['diff', '-u', '--binary', 'old', 'new']
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('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')
251
333
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
stdin=subprocess.PIPE)
334
stdin=subprocess.PIPE)
253
335
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
256
336
# We should output whatever diff tells us, plus a trailing newline
257
self.assertEqual(out.splitlines(True) + ['\n'], lines)
260
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
261
"""Has a helper for running show_diff_trees"""
263
def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
265
if working_tree is not None:
266
extra_trees = (working_tree,)
269
diff.show_diff_trees(tree1, tree2, output,
270
specific_files=specific_files,
271
extra_trees=extra_trees, old_label='old/',
273
return output.getvalue()
276
class TestDiffDates(TestShowDiffTreesHelper):
337
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
340
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
342
if working_tree is not None:
343
extra_trees = (working_tree,)
346
diff.show_diff_trees(tree1, tree2, output,
347
specific_files=specific_files,
348
extra_trees=extra_trees, old_label='old/',
350
return output.getvalue()
353
class TestDiffDates(tests.TestCaseWithTransport):
279
356
super(TestDiffDates, self).setUp()
280
357
self.wt = self.make_branch_and_tree('.')
281
358
self.b = self.wt.branch
282
359
self.build_tree_contents([
283
('file1', 'file1 contents at rev 1\n'),
284
('file2', 'file2 contents at rev 1\n')
360
('file1', b'file1 contents at rev 1\n'),
361
('file2', b'file2 contents at rev 1\n')
286
363
self.wt.add(['file1', 'file2'])
288
365
message='Revision 1',
289
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
366
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
292
self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
369
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
294
371
message='Revision 2',
295
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
372
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
298
self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
375
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
300
377
message='Revision 3',
301
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
378
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
304
381
self.wt.remove(['file2'])
306
383
message='Revision 4',
307
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
384
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
310
387
self.build_tree_contents([
311
('file1', 'file1 contents in working tree\n')
388
('file1', b'file1 contents in working tree\n')
313
390
# set the date stamps for files in the working tree to known values
314
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
316
393
def test_diff_rev_tree_working_tree(self):
317
output = self.get_diff(self.wt.basis_tree(), self.wt)
394
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
395
# note that the date for old/file1 is from rev 2 rather than from
319
396
# the basis revision (rev 4)
320
self.assertEqualDiff(output, '''\
397
self.assertEqualDiff(output, b'''\
321
398
=== modified file 'file1'
322
399
--- old/file1\t2006-04-02 00:00:00 +0000
323
400
+++ new/file1\t2006-04-05 00:00:00 +0000
393
470
self.wt.add(['dir1', 'dir2'])
394
471
self.wt.rename_one('file1', 'dir1/file1')
395
old_tree = self.b.repository.revision_tree('rev-1')
396
new_tree = self.b.repository.revision_tree('rev-4')
397
out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
398
working_tree=self.wt)
399
self.assertContainsRe(out, 'file1\t')
400
out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
401
working_tree=self.wt)
402
self.assertNotContainsRe(out, 'file1\t')
406
class TestShowDiffTrees(TestShowDiffTreesHelper):
472
old_tree = self.b.repository.revision_tree(b'rev-1')
473
new_tree = self.b.repository.revision_tree(b'rev-4')
474
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
475
working_tree=self.wt)
476
self.assertContainsRe(out, b'file1\t')
477
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
478
working_tree=self.wt)
479
self.assertNotContainsRe(out, b'file1\t')
482
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
483
"""Direct tests for show_diff_trees"""
409
485
def test_modified_file(self):
410
486
"""Test when a file is modified."""
411
487
tree = self.make_branch_and_tree('tree')
412
self.build_tree_contents([('tree/file', 'contents\n')])
413
tree.add(['file'], ['file-id'])
414
tree.commit('one', rev_id='rev-1')
488
self.build_tree_contents([('tree/file', b'contents\n')])
489
tree.add(['file'], [b'file-id'])
490
tree.commit('one', rev_id=b'rev-1')
416
self.build_tree_contents([('tree/file', 'new contents\n')])
417
d = self.get_diff(tree.basis_tree(), tree)
418
self.assertContainsRe(d, "=== modified file 'file'\n")
419
self.assertContainsRe(d, '--- old/file\t')
420
self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
421
self.assertContainsRe(d, '-contents\n'
492
self.build_tree_contents([('tree/file', b'new contents\n')])
493
d = get_diff_as_string(tree.basis_tree(), tree)
494
self.assertContainsRe(d, b"=== modified file 'file'\n")
495
self.assertContainsRe(d, b'--- old/file\t')
496
self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
497
self.assertContainsRe(d, b'-contents\n'
498
b'\\+new contents\n')
424
500
def test_modified_file_in_renamed_dir(self):
425
501
"""Test when a file is modified in a renamed directory."""
426
502
tree = self.make_branch_and_tree('tree')
427
503
self.build_tree(['tree/dir/'])
428
self.build_tree_contents([('tree/dir/file', 'contents\n')])
429
tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
430
tree.commit('one', rev_id='rev-1')
504
self.build_tree_contents([('tree/dir/file', b'contents\n')])
505
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
506
tree.commit('one', rev_id=b'rev-1')
432
508
tree.rename_one('dir', 'other')
433
self.build_tree_contents([('tree/other/file', 'new contents\n')])
434
d = self.get_diff(tree.basis_tree(), tree)
435
self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
436
self.assertContainsRe(d, "=== modified file 'other/file'\n")
509
self.build_tree_contents([('tree/other/file', b'new contents\n')])
510
d = get_diff_as_string(tree.basis_tree(), tree)
511
self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
512
self.assertContainsRe(d, b"=== modified file 'other/file'\n")
437
513
# XXX: This is technically incorrect, because it used to be at another
438
514
# location. What to do?
439
self.assertContainsRe(d, '--- old/dir/file\t')
440
self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
441
self.assertContainsRe(d, '-contents\n'
515
self.assertContainsRe(d, b'--- old/dir/file\t')
516
self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
517
self.assertContainsRe(d, b'-contents\n'
518
b'\\+new contents\n')
444
520
def test_renamed_directory(self):
445
521
"""Test when only a directory is only renamed."""
446
522
tree = self.make_branch_and_tree('tree')
447
523
self.build_tree(['tree/dir/'])
448
self.build_tree_contents([('tree/dir/file', 'contents\n')])
449
tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
450
tree.commit('one', rev_id='rev-1')
524
self.build_tree_contents([('tree/dir/file', b'contents\n')])
525
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
526
tree.commit('one', rev_id=b'rev-1')
452
528
tree.rename_one('dir', 'newdir')
453
d = self.get_diff(tree.basis_tree(), tree)
529
d = get_diff_as_string(tree.basis_tree(), tree)
454
530
# Renaming a directory should be a single "you renamed this dir" even
455
531
# when there are files inside.
456
self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
532
self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
458
534
def test_renamed_file(self):
459
535
"""Test when a file is only renamed."""
460
536
tree = self.make_branch_and_tree('tree')
461
self.build_tree_contents([('tree/file', 'contents\n')])
462
tree.add(['file'], ['file-id'])
463
tree.commit('one', rev_id='rev-1')
537
self.build_tree_contents([('tree/file', b'contents\n')])
538
tree.add(['file'], [b'file-id'])
539
tree.commit('one', rev_id=b'rev-1')
465
541
tree.rename_one('file', 'newname')
466
d = self.get_diff(tree.basis_tree(), tree)
467
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
542
d = get_diff_as_string(tree.basis_tree(), tree)
543
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
468
544
# We shouldn't have a --- or +++ line, because there is no content
470
self.assertNotContainsRe(d, '---')
546
self.assertNotContainsRe(d, b'---')
472
548
def test_renamed_and_modified_file(self):
473
549
"""Test when a file is only renamed."""
474
550
tree = self.make_branch_and_tree('tree')
475
self.build_tree_contents([('tree/file', 'contents\n')])
476
tree.add(['file'], ['file-id'])
477
tree.commit('one', rev_id='rev-1')
551
self.build_tree_contents([('tree/file', b'contents\n')])
552
tree.add(['file'], [b'file-id'])
553
tree.commit('one', rev_id=b'rev-1')
479
555
tree.rename_one('file', 'newname')
480
self.build_tree_contents([('tree/newname', 'new contents\n')])
481
d = self.get_diff(tree.basis_tree(), tree)
482
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
self.assertContainsRe(d, '--- old/file\t')
484
self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
485
self.assertContainsRe(d, '-contents\n'
556
self.build_tree_contents([('tree/newname', b'new contents\n')])
557
d = get_diff_as_string(tree.basis_tree(), tree)
558
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
559
self.assertContainsRe(d, b'--- old/file\t')
560
self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
561
self.assertContainsRe(d, b'-contents\n'
562
b'\\+new contents\n')
489
564
def test_internal_diff_exec_property(self):
490
565
tree = self.make_branch_and_tree('tree')
492
567
tt = transform.TreeTransform(tree)
493
tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
494
tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
495
tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
496
tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
497
tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
498
tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
568
tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
569
tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
570
tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
571
tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
572
tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
573
tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
500
tree.commit('one', rev_id='rev-1')
575
tree.commit('one', rev_id=b'rev-1')
502
577
tt = transform.TreeTransform(tree)
503
tt.set_executability(False, tt.trans_id_file_id('a-id'))
504
tt.set_executability(True, tt.trans_id_file_id('b-id'))
505
tt.set_executability(False, tt.trans_id_file_id('c-id'))
506
tt.set_executability(True, tt.trans_id_file_id('d-id'))
578
tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
579
tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
580
tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
581
tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
508
583
tree.rename_one('c', 'new-c')
509
584
tree.rename_one('d', 'new-d')
511
d = self.get_diff(tree.basis_tree(), tree)
513
self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
515
self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
517
self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
519
self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
521
self.assertNotContainsRe(d, r"file 'e'")
522
self.assertNotContainsRe(d, r"file 'f'")
586
d = get_diff_as_string(tree.basis_tree(), tree)
588
self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
590
self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
592
self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
594
self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
596
self.assertNotContainsRe(d, br"file 'e'")
597
self.assertNotContainsRe(d, br"file 'f'")
525
599
def test_binary_unicode_filenames(self):
526
600
"""Test that contents of files are *not* encoded in UTF-8 when there
527
601
is a binary file in the diff.
529
603
# See https://bugs.launchpad.net/bugs/110092.
530
self.requireFeature(tests.UnicodeFilenameFeature)
604
self.requireFeature(features.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
534
606
tree = self.make_branch_and_tree('tree')
535
607
alpha, omega = u'\u03b1', u'\u03c9'
536
608
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
537
609
self.build_tree_contents(
538
[('tree/' + alpha, chr(0)),
610
[('tree/' + alpha, b'\0'),
539
611
('tree/' + omega,
540
('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
541
tree.add([alpha], ['file-id'])
542
tree.add([omega], ['file-id-2'])
543
diff_content = StringIO()
612
(b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
613
tree.add([alpha], [b'file-id'])
614
tree.add([omega], [b'file-id-2'])
615
diff_content = StubO()
544
616
diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
545
d = diff_content.getvalue()
546
self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
547
self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
617
diff_content.check_types(self, bytes)
618
d = b''.join(diff_content.write_record)
619
self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
620
self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
548
621
% (alpha_utf8, alpha_utf8))
549
self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
550
self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
551
self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
622
self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
623
self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
624
self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
553
626
def test_unicode_filename(self):
554
627
"""Test when the filename are unicode."""
555
self.requireFeature(tests.UnicodeFilenameFeature)
628
self.requireFeature(features.UnicodeFilenameFeature)
557
630
alpha, omega = u'\u03b1', u'\u03c9'
558
631
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
560
633
tree = self.make_branch_and_tree('tree')
561
self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
562
tree.add(['ren_'+alpha], ['file-id-2'])
563
self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
564
tree.add(['del_'+alpha], ['file-id-3'])
565
self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
566
tree.add(['mod_'+alpha], ['file-id-4'])
568
tree.commit('one', rev_id='rev-1')
570
tree.rename_one('ren_'+alpha, 'ren_'+omega)
571
tree.remove('del_'+alpha)
572
self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
573
tree.add(['add_'+alpha], ['file-id'])
574
self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
576
d = self.get_diff(tree.basis_tree(), tree)
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'])
641
tree.commit('one', rev_id=b'rev-1')
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')])
649
d = get_diff_as_string(tree.basis_tree(), tree)
577
650
self.assertContainsRe(d,
578
"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
self.assertContainsRe(d, "=== 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)
656
def test_unicode_filename_path_encoding(self):
657
"""Test for bug #382699: unicode filenames on Windows should be shown
660
self.requireFeature(features.UnicodeFilenameFeature)
661
# The word 'test' in Russian
662
_russian_test = u'\u0422\u0435\u0441\u0442'
663
directory = _russian_test + u'/'
664
test_txt = _russian_test + u'.txt'
665
u1234 = u'\u1234.txt'
667
tree = self.make_branch_and_tree('.')
668
self.build_tree_contents([
669
(test_txt, b'foo\n'),
673
tree.add([test_txt, u1234, directory])
676
diff.show_diff_trees(tree.basis_tree(), tree, sio,
677
path_encoding='cp1251')
679
output = subst_dates(sio.getvalue())
681
=== added directory '%(directory)s'
682
=== added file '%(test_txt)s'
683
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
684
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
688
=== added file '?.txt'
689
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
690
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
694
''' % {b'directory': _russian_test.encode('cp1251'),
695
b'test_txt': test_txt.encode('cp1251'),
697
self.assertEqualDiff(output, shouldbe)
584
700
class DiffWasIs(diff.DiffPath):
586
702
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
587
self.to_file.write('was: ')
588
self.to_file.write(self.old_tree.get_file(file_id).read())
589
self.to_file.write('is: ')
590
self.to_file.write(self.new_tree.get_file(file_id).read())
703
self.to_file.write(b'was: ')
704
self.to_file.write(self.old_tree.get_file(old_path).read())
705
self.to_file.write(b'is: ')
706
self.to_file.write(self.new_tree.get_file(new_path).read())
594
709
class TestDiffTree(tests.TestCaseWithTransport):
601
716
self.new_tree = self.make_branch_and_tree('new-tree')
602
717
self.new_tree.lock_write()
603
718
self.addCleanup(self.new_tree.unlock)
604
self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
719
self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
606
721
def test_diff_text(self):
607
722
self.build_tree_contents([('old-tree/olddir/',),
608
('old-tree/olddir/oldfile', 'old\n')])
723
('old-tree/olddir/oldfile', b'old\n')])
609
724
self.old_tree.add('olddir')
610
self.old_tree.add('olddir/oldfile', 'file-id')
725
self.old_tree.add('olddir/oldfile', b'file-id')
611
726
self.build_tree_contents([('new-tree/newdir/',),
612
('new-tree/newdir/newfile', 'new\n')])
727
('new-tree/newdir/newfile', b'new\n')])
613
728
self.new_tree.add('newdir')
614
self.new_tree.add('newdir/newfile', 'file-id')
615
differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
616
differ.diff_text('file-id', None, 'old label', 'new label')
618
'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
619
differ.to_file.getvalue())
620
differ.to_file.seek(0)
621
differ.diff_text(None, 'file-id', 'old label', 'new label')
623
'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
624
differ.to_file.getvalue())
625
differ.to_file.seek(0)
626
differ.diff_text('file-id', 'file-id', 'old label', 'new label')
628
'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
729
self.new_tree.add('newdir/newfile', b'file-id')
730
differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
731
differ.diff_text('olddir/oldfile', None, 'old label',
732
'new label', b'file-id', None)
734
b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
735
differ.to_file.getvalue())
736
differ.to_file.seek(0)
737
differ.diff_text(None, 'newdir/newfile',
738
'old label', 'new label', None, b'file-id')
740
b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
741
differ.to_file.getvalue())
742
differ.to_file.seek(0)
743
differ.diff_text('olddir/oldfile', 'newdir/newfile',
744
'old label', 'new label', b'file-id', b'file-id')
746
b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
629
747
differ.to_file.getvalue())
631
749
def test_diff_deletion(self):
632
self.build_tree_contents([('old-tree/file', 'contents'),
633
('new-tree/file', 'contents')])
634
self.old_tree.add('file', 'file-id')
635
self.new_tree.add('file', 'file-id')
750
self.build_tree_contents([('old-tree/file', b'contents'),
751
('new-tree/file', b'contents')])
752
self.old_tree.add('file', b'file-id')
753
self.new_tree.add('file', b'file-id')
636
754
os.unlink('new-tree/file')
637
755
self.differ.show_diff(None)
638
self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
756
self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
640
758
def test_diff_creation(self):
641
self.build_tree_contents([('old-tree/file', 'contents'),
642
('new-tree/file', 'contents')])
643
self.old_tree.add('file', 'file-id')
644
self.new_tree.add('file', 'file-id')
759
self.build_tree_contents([('old-tree/file', b'contents'),
760
('new-tree/file', b'contents')])
761
self.old_tree.add('file', b'file-id')
762
self.new_tree.add('file', b'file-id')
645
763
os.unlink('old-tree/file')
646
764
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
765
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
649
767
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
768
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
769
differ.diff_symlink('old target', None)
652
self.assertEqual("=== target was 'old target'\n",
770
self.assertEqual(b"=== target was 'old target'\n",
653
771
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
773
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
774
differ.diff_symlink(None, 'new target')
657
self.assertEqual("=== target is 'new target'\n",
775
self.assertEqual(b"=== target is 'new target'\n",
658
776
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
778
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
779
differ.diff_symlink('old target', 'new target')
662
self.assertEqual("=== target changed 'old target' => 'new target'\n",
780
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
663
781
differ.to_file.getvalue())
665
783
def test_diff(self):
666
784
self.build_tree_contents([('old-tree/olddir/',),
667
('old-tree/olddir/oldfile', 'old\n')])
785
('old-tree/olddir/oldfile', b'old\n')])
668
786
self.old_tree.add('olddir')
669
self.old_tree.add('olddir/oldfile', 'file-id')
787
self.old_tree.add('olddir/oldfile', b'file-id')
670
788
self.build_tree_contents([('new-tree/newdir/',),
671
('new-tree/newdir/newfile', 'new\n')])
789
('new-tree/newdir/newfile', b'new\n')])
672
790
self.new_tree.add('newdir')
673
self.new_tree.add('newdir/newfile', 'file-id')
674
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
791
self.new_tree.add('newdir/newfile', b'file-id')
792
self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
675
793
self.assertContainsRe(
676
794
self.differ.to_file.getvalue(),
677
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
' \@\@\n-old\n\+new\n\n')
795
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
796
br' \@\@\n-old\n\+new\n\n')
680
798
def test_diff_kind_change(self):
681
self.requireFeature(tests.SymlinkFeature)
799
self.requireFeature(features.SymlinkFeature)
682
800
self.build_tree_contents([('old-tree/olddir/',),
683
('old-tree/olddir/oldfile', 'old\n')])
801
('old-tree/olddir/oldfile', b'old\n')])
684
802
self.old_tree.add('olddir')
685
self.old_tree.add('olddir/oldfile', 'file-id')
803
self.old_tree.add('olddir/oldfile', b'file-id')
686
804
self.build_tree(['new-tree/newdir/'])
687
805
os.symlink('new', 'new-tree/newdir/newfile')
688
806
self.new_tree.add('newdir')
689
self.new_tree.add('newdir/newfile', 'file-id')
690
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
807
self.new_tree.add('newdir/newfile', b'file-id')
808
self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
691
809
self.assertContainsRe(
692
810
self.differ.to_file.getvalue(),
693
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
811
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
695
813
self.assertContainsRe(self.differ.to_file.getvalue(),
696
"=== target is u'new'\n")
814
b"=== target is 'new'\n")
698
816
def test_diff_directory(self):
699
817
self.build_tree(['new-tree/new-dir/'])
700
self.new_tree.add('new-dir', 'new-dir-id')
701
self.differ.diff('new-dir-id', None, 'new-dir')
702
self.assertEqual(self.differ.to_file.getvalue(), '')
818
self.new_tree.add('new-dir', b'new-dir-id')
819
self.differ.diff(b'new-dir-id', None, 'new-dir')
820
self.assertEqual(self.differ.to_file.getvalue(), b'')
704
822
def create_old_new(self):
705
823
self.build_tree_contents([('old-tree/olddir/',),
706
('old-tree/olddir/oldfile', 'old\n')])
824
('old-tree/olddir/oldfile', b'old\n')])
707
825
self.old_tree.add('olddir')
708
self.old_tree.add('olddir/oldfile', 'file-id')
826
self.old_tree.add('olddir/oldfile', b'file-id')
709
827
self.build_tree_contents([('new-tree/newdir/',),
710
('new-tree/newdir/newfile', 'new\n')])
828
('new-tree/newdir/newfile', b'new\n')])
711
829
self.new_tree.add('newdir')
712
self.new_tree.add('newdir/newfile', 'file-id')
830
self.new_tree.add('newdir/newfile', b'file-id')
714
832
def test_register_diff(self):
715
833
self.create_old_new()
716
834
old_diff_factories = diff.DiffTree.diff_factories
717
diff.DiffTree.diff_factories=old_diff_factories[:]
835
diff.DiffTree.diff_factories = old_diff_factories[:]
718
836
diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
720
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
838
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
722
840
diff.DiffTree.diff_factories = old_diff_factories
723
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
841
differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
724
842
self.assertNotContainsRe(
725
843
differ.to_file.getvalue(),
726
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
' \@\@\n-old\n\+new\n\n')
844
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
845
br' \@\@\n-old\n\+new\n\n')
728
846
self.assertContainsRe(differ.to_file.getvalue(),
729
'was: old\nis: new\n')
847
b'was: old\nis: new\n')
731
849
def test_extra_factories(self):
732
850
self.create_old_new()
733
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
851
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
734
852
extra_factories=[DiffWasIs.from_diff_tree])
735
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
853
differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
736
854
self.assertNotContainsRe(
737
855
differ.to_file.getvalue(),
738
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
' \@\@\n-old\n\+new\n\n')
856
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
857
br' \@\@\n-old\n\+new\n\n')
740
858
self.assertContainsRe(differ.to_file.getvalue(),
741
'was: old\nis: new\n')
859
b'was: old\nis: new\n')
743
861
def test_alphabetical_order(self):
744
862
self.build_tree(['new-tree/a-file'])
764
882
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
883
sm = self._PatienceSequenceMatcher(None, a, b)
766
884
mb = sm.get_matching_blocks()
767
self.assertEquals(35, len(mb))
885
self.assertEqual(35, len(mb))
769
887
def test_unique_lcs(self):
770
888
unique_lcs = self._unique_lcs
771
self.assertEquals(unique_lcs('', ''), [])
772
self.assertEquals(unique_lcs('', 'a'), [])
773
self.assertEquals(unique_lcs('a', ''), [])
774
self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
775
self.assertEquals(unique_lcs('a', 'b'), [])
776
self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
777
self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
778
self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
779
self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
781
self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
889
self.assertEqual(unique_lcs('', ''), [])
890
self.assertEqual(unique_lcs('', 'a'), [])
891
self.assertEqual(unique_lcs('a', ''), [])
892
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
893
self.assertEqual(unique_lcs('a', 'b'), [])
894
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
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)])
899
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
901
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
783
903
def test_recurse_matches(self):
784
904
def test_one(a, b, matches):
785
905
test_matches = []
786
906
self._recurse_matches(
787
907
a, b, 0, 0, len(a), len(b), test_matches, 10)
788
self.assertEquals(test_matches, matches)
908
self.assertEqual(test_matches, matches)
790
910
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
911
[(0, 0), (2, 2), (4, 4)])
896
1017
def test_opcodes(self):
897
1018
def chk_ops(a, b, expected_codes):
898
1019
s = self._PatienceSequenceMatcher(None, a, b)
899
self.assertEquals(expected_codes, s.get_opcodes())
1020
self.assertEqual(expected_codes, s.get_opcodes())
901
1022
chk_ops('', '', [])
902
1023
chk_ops([], [], [])
903
chk_ops('abc', '', [('delete', 0,3, 0,0)])
904
chk_ops('', 'abc', [('insert', 0,0, 0,3)])
905
chk_ops('abcd', 'abcd', [('equal', 0,4, 0,4)])
906
chk_ops('abcd', 'abce', [('equal', 0,3, 0,3),
907
('replace', 3,4, 3,4)
909
chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
913
chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
916
chk_ops('abcde', 'abXde', [('equal', 0,2, 0,2),
917
('replace', 2,3, 2,3),
1024
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1025
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1026
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1027
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1028
('replace', 3, 4, 3, 4)
1030
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1031
('equal', 1, 4, 0, 3),
1032
('insert', 4, 4, 3, 4)
1034
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1035
('equal', 1, 5, 0, 4)
920
chk_ops('abcde', 'abXYZde', [('equal', 0,2, 0,2),
921
('replace', 2,3, 2,5),
1037
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1038
('replace', 2, 3, 2, 3),
1039
('equal', 3, 5, 3, 5)
1041
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1042
('replace', 2, 3, 2, 5),
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)
924
chk_ops('abde', 'abXYZde', [('equal', 0,2, 0,2),
925
('insert', 2,2, 2,5),
928
1049
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
[('equal', 0,6, 0,6),
930
('insert', 6,6, 6,11),
931
('equal', 6,16, 11,21)
1050
[('equal', 0, 6, 0, 6),
1051
('insert', 6, 6, 6, 11),
1052
('equal', 6, 16, 11, 21)
936
, 'how are you today?\n'],
938
, 'how are you today?\n'],
939
[('equal', 0,1, 0,1),
940
('delete', 1,2, 1,1),
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),
943
1061
chk_ops('aBccDe', 'abccde',
944
[('equal', 0,1, 0,1),
945
('replace', 1,5, 1,5),
1062
[('equal', 0, 1, 0, 1),
1063
('replace', 1, 5, 1, 5),
1064
('equal', 5, 6, 5, 6),
948
1066
chk_ops('aBcDec', 'abcdec',
949
[('equal', 0,1, 0,1),
950
('replace', 1,2, 1,2),
952
('replace', 3,4, 3,4),
1067
[('equal', 0, 1, 0, 1),
1068
('replace', 1, 2, 1, 2),
1069
('equal', 2, 3, 2, 3),
1070
('replace', 3, 4, 3, 4),
1071
('equal', 4, 6, 4, 6),
955
1073
chk_ops('aBcdEcdFg', 'abcdecdfg',
956
[('equal', 0,1, 0,1),
957
('replace', 1,8, 1,8),
1074
[('equal', 0, 1, 0, 1),
1075
('replace', 1, 8, 1, 8),
1076
('equal', 8, 9, 8, 9)
960
1078
chk_ops('aBcdEeXcdFg', 'abcdecdfg',
961
[('equal', 0,1, 0,1),
962
('replace', 1,2, 1,2),
964
('delete', 4,5, 4,4),
966
('delete', 6,7, 5,5),
968
('replace', 9,10, 7,8),
969
('equal', 10,11, 8,9)
1079
[('equal', 0, 1, 0, 1),
1080
('replace', 1, 2, 1, 2),
1081
('equal', 2, 4, 2, 4),
1082
('delete', 4, 5, 4, 4),
1083
('equal', 5, 6, 4, 5),
1084
('delete', 6, 7, 5, 5),
1085
('equal', 7, 9, 5, 7),
1086
('replace', 9, 10, 7, 8),
1087
('equal', 10, 11, 8, 9)
972
1090
def test_grouped_opcodes(self):
973
1091
def chk_ops(a, b, expected_codes, n=3):
974
1092
s = self._PatienceSequenceMatcher(None, a, b)
975
self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
1093
self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
977
1095
chk_ops('', '', [])
978
1096
chk_ops([], [], [])
979
chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
980
chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
1097
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1098
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
981
1099
chk_ops('abcd', 'abcd', [])
982
chk_ops('abcd', 'abce', [[('equal', 0,3, 0,3),
983
('replace', 3,4, 3,4)
985
chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
1100
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1101
('replace', 3, 4, 3, 4)
1103
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1104
('equal', 1, 4, 0, 3),
1105
('insert', 4, 4, 3, 4)
989
1107
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
990
[[('equal', 3,6, 3,6),
991
('insert', 6,6, 6,11),
992
('equal', 6,9, 11,14)
1108
[[('equal', 3, 6, 3, 6),
1109
('insert', 6, 6, 6, 11),
1110
('equal', 6, 9, 11, 14)
994
1112
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
995
[[('equal', 2,6, 2,6),
996
('insert', 6,6, 6,11),
997
('equal', 6,10, 11,15)
1113
[[('equal', 2, 6, 2, 6),
1114
('insert', 6, 6, 6, 11),
1115
('equal', 6, 10, 11, 15)
999
1117
chk_ops('Xabcdef', 'abcdef',
1000
[[('delete', 0,1, 0,0),
1118
[[('delete', 0, 1, 0, 0),
1119
('equal', 1, 4, 0, 3)
1003
1121
chk_ops('abcdef', 'abcdefX',
1004
[[('equal', 3,6, 3,6),
1005
('insert', 6,6, 6,7)
1122
[[('equal', 3, 6, 3, 6),
1123
('insert', 6, 6, 6, 7)
1009
1126
def test_multiple_ranges(self):
1010
1127
# There was an earlier bug where we used a bad set of ranges,
1011
1128
# this triggers that specific bug, to make sure it doesn't regress
1356
1469
self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1358
1471
def test_prepare_files(self):
1360
1473
tree = self.make_branch_and_tree('tree')
1361
self.build_tree_contents([('tree/oldname', 'oldcontent')])
1362
self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1363
tree.add('oldname', 'file-id')
1364
tree.add('oldname2', 'file2-id')
1474
self.build_tree_contents([('tree/oldname', b'oldcontent')])
1475
self.build_tree_contents([('tree/oldname2', b'oldcontent2')])
1476
tree.add('oldname', b'file-id')
1477
tree.add('oldname2', b'file2-id')
1365
1478
# Earliest allowable date on FAT32 filesystems is 1980-01-01
1366
1479
tree.commit('old tree', timestamp=315532800)
1367
1480
tree.rename_one('oldname', 'newname')
1368
1481
tree.rename_one('oldname2', 'newname2')
1369
self.build_tree_contents([('tree/newname', 'newcontent')])
1370
self.build_tree_contents([('tree/newname2', 'newcontent2')])
1482
self.build_tree_contents([('tree/newname', b'newcontent')])
1483
self.build_tree_contents([('tree/newname2', b'newcontent2')])
1371
1484
old_tree = tree.basis_tree()
1372
1485
old_tree.lock_read()
1373
1486
self.addCleanup(old_tree.unlock)
1374
1487
tree.lock_read()
1375
1488
self.addCleanup(tree.unlock)
1376
diff_obj = diff.DiffFromTool(['python', '-c',
1489
diff_obj = diff.DiffFromTool([sys.executable, '-c',
1377
1490
'print "@old_path @new_path"'],
1378
1491
old_tree, tree, output)
1379
1492
self.addCleanup(diff_obj.finish)
1380
self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1381
old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1493
self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
1494
old_path, new_path = diff_obj._prepare_files(
1495
'oldname', 'newname', file_id=b'file-id')
1383
1496
self.assertContainsRe(old_path, 'old/oldname$')
1384
1497
self.assertEqual(315532800, os.stat(old_path).st_mtime)
1385
1498
self.assertContainsRe(new_path, 'tree/newname$')
1386
self.assertFileEqual('oldcontent', old_path)
1387
self.assertFileEqual('newcontent', new_path)
1499
self.assertFileEqual(b'oldcontent', old_path)
1500
self.assertFileEqual(b'newcontent', new_path)
1388
1501
if osutils.host_os_dereferences_symlinks():
1389
1502
self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
1503
# make sure we can create files with the same parent directories
1391
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1504
diff_obj._prepare_files('oldname2', 'newname2', file_id=b'file2-id')
1507
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1509
def test_encodable_filename(self):
1510
# Just checks file path for external diff tool.
1511
# We cannot change CPython's internal encoding used by os.exec*.
1512
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1514
for _, scenario in EncodingAdapter.encoding_scenarios:
1515
encoding = scenario['encoding']
1516
dirname = scenario['info']['directory']
1517
filename = scenario['info']['filename']
1519
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1520
relpath = dirname + u'/' + filename
1521
fullpath = diffobj._safe_filename('safe', relpath)
1522
self.assertEqual(fullpath,
1523
fullpath.encode(encoding).decode(encoding))
1524
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1526
def test_unencodable_filename(self):
1527
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1529
for _, scenario in EncodingAdapter.encoding_scenarios:
1530
encoding = scenario['encoding']
1531
dirname = scenario['info']['directory']
1532
filename = scenario['info']['filename']
1534
if encoding == 'iso-8859-1':
1535
encoding = 'iso-8859-2'
1537
encoding = 'iso-8859-1'
1539
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1540
relpath = dirname + u'/' + filename
1541
fullpath = diffobj._safe_filename('safe', relpath)
1542
self.assertEqual(fullpath,
1543
fullpath.encode(encoding).decode(encoding))
1544
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1394
1547
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1396
1549
def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1397
"""Call get_trees_and_branches_to_diff_locked. Overridden by
1398
TestGetTreesAndBranchesToDiff.
1550
"""Call get_trees_and_branches_to_diff_locked."""
1400
1551
return diff.get_trees_and_branches_to_diff_locked(
1401
1552
path_list, revision_specs, old_url, new_url, self.addCleanup)