77
"""Simple file-like object that allows writes with any type and records."""
80
self.write_record = []
82
def write(self, data):
83
self.write_record.append(data)
85
def check_types(self, testcase, expected_type):
87
any(not isinstance(o, expected_type) for o in self.write_record),
88
"Not all writes of type %s: %r" % (
89
expected_type.__name__, self.write_record))
92
class TestDiffOptions(tests.TestCase):
94
def test_unified_added(self):
95
"""Check for default style '-u' only if no other style specified
98
# Verify that style defaults to unified, id est '-u' appended
99
# to option list, in the absence of an alternative style.
100
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
103
class TestDiffOptionsScenarios(tests.TestCase):
105
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
106
style = None # Set by load_tests_apply_scenarios from scenarios
108
def test_unified_not_added(self):
109
# Verify that for all valid style options, '-u' is not
110
# appended to option list.
111
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
112
self.assertEqual(["%s" % (self.style,)], ret_opts)
115
83
class TestDiff(tests.TestCase):
117
85
def test_add_nl(self):
118
86
"""diff generates a valid diff for patches that add a newline"""
119
lines = udiff_lines([b'boo'], [b'boo\n'])
87
lines = udiff_lines(['boo'], ['boo\n'])
120
88
self.check_patch(lines)
121
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
122
## "expected no-nl, got %r" % lines[4]
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
90
## "expected no-nl, got %r" % lines[4]
124
92
def test_add_nl_2(self):
125
93
"""diff generates a valid diff for patches that change last line and
128
lines = udiff_lines([b'boo'], [b'goo\n'])
96
lines = udiff_lines(['boo'], ['goo\n'])
129
97
self.check_patch(lines)
130
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
131
## "expected no-nl, got %r" % lines[4]
98
self.assertEquals(lines[4], '\\ No newline at end of file\n')
99
## "expected no-nl, got %r" % lines[4]
133
101
def test_remove_nl(self):
134
102
"""diff generates a valid diff for patches that change last line and
137
lines = udiff_lines([b'boo\n'], [b'boo'])
105
lines = udiff_lines(['boo\n'], ['boo'])
138
106
self.check_patch(lines)
139
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
140
## "expected no-nl, got %r" % lines[5]
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
108
## "expected no-nl, got %r" % lines[5]
142
110
def check_patch(self, lines):
143
self.assertTrue(len(lines) > 1)
144
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
145
self.assertTrue(lines[0].startswith(b'---'))
146
## 'No orig line for patch:\n%s' % "".join(lines)
147
self.assertTrue(lines[1].startswith(b'+++'))
148
## 'No mod line for patch:\n%s' % "".join(lines)
149
self.assertTrue(len(lines) > 2)
150
## "No hunks for patch:\n%s" % "".join(lines)
151
self.assertTrue(lines[2].startswith(b'@@'))
152
## "No hunk header for patch:\n%s" % "".join(lines)
153
self.assertTrue(b'@@' in lines[2][2:])
154
## "Unterminated hunk header for patch:\n%s" % "".join(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)
156
124
def test_binary_lines(self):
158
uni_lines = [1023 * b'a' + b'\x00']
159
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
126
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
160
128
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
161
udiff_lines(uni_lines, empty, allow_binary=True)
129
udiff_lines(uni_lines , empty, allow_binary=True)
162
130
udiff_lines(empty, uni_lines, allow_binary=True)
164
132
def test_external_diff(self):
165
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
133
lines = external_udiff_lines(['boo\n'], ['goo\n'])
166
134
self.check_patch(lines)
167
self.assertEqual(b'\n', lines[-1])
135
self.assertEqual('\n', lines[-1])
169
137
def test_external_diff_no_fileno(self):
170
138
# Make sure that we can handle not having a fileno, even
171
139
# if the diff is large
172
lines = external_udiff_lines([b'boo\n'] * 10000,
140
lines = external_udiff_lines(['boo\n']*10000,
174
142
use_stringio=True)
175
143
self.check_patch(lines)
177
145
def test_external_diff_binary_lang_c(self):
178
147
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
179
self.overrideEnv(lang, 'C')
180
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
181
# Older versions of diffutils say "Binary files", newer
182
# versions just say "Files".
183
self.assertContainsRe(
184
lines[0], b'(Binary f|F)iles old and new differ\n')
185
self.assertEqual(lines[1:], [b'\n'])
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)
187
160
def test_no_external_diff(self):
188
161
"""Check that NoDiff is raised when diff is not available"""
189
# Make sure no 'diff' command is available
190
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
191
self.overrideEnv('PATH', '')
192
self.assertRaises(errors.NoDiff, diff.external_diff,
193
b'old', [b'boo\n'], b'new', [b'goo\n'],
194
BytesIO(), diff_opts=['-u'])
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
196
172
def test_internal_diff_default(self):
197
173
# Default internal diff encoding is utf8
199
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
200
u'new_\xe5', [b'new_text\n'], output)
175
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
u'new_\xe5', ['new_text\n'], output)
201
177
lines = output.getvalue().splitlines(True)
202
178
self.check_patch(lines)
203
self.assertEqual([b'--- old_\xc2\xb5\n',
204
b'+++ new_\xc3\xa5\n',
205
b'@@ -1,1 +1,1 @@\n',
179
self.assertEquals(['--- old_\xc2\xb5\n',
180
'+++ new_\xc3\xa5\n',
211
188
def test_internal_diff_utf8(self):
213
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
214
u'new_\xe5', [b'new_text\n'], output,
190
diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
u'new_\xe5', ['new_text\n'], output,
215
192
path_encoding='utf8')
216
193
lines = output.getvalue().splitlines(True)
217
194
self.check_patch(lines)
218
self.assertEqual([b'--- old_\xc2\xb5\n',
219
b'+++ new_\xc3\xa5\n',
220
b'@@ -1,1 +1,1 @@\n',
195
self.assertEquals(['--- old_\xc2\xb5\n',
196
'+++ new_\xc3\xa5\n',
226
204
def test_internal_diff_iso_8859_1(self):
228
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
229
u'new_\xe5', [b'new_text\n'], output,
206
diff.internal_diff(u'old_\xb5', ['old_text\n'],
207
u'new_\xe5', ['new_text\n'], output,
230
208
path_encoding='iso-8859-1')
231
209
lines = output.getvalue().splitlines(True)
232
210
self.check_patch(lines)
233
self.assertEqual([b'--- old_\xb5\n',
235
b'@@ -1,1 +1,1 @@\n',
211
self.assertEquals(['--- old_\xb5\n',
241
220
def test_internal_diff_no_content(self):
243
222
diff.internal_diff(u'old', [], u'new', [], output)
244
self.assertEqual(b'', output.getvalue())
223
self.assertEqual('', output.getvalue())
246
225
def test_internal_diff_no_changes(self):
248
diff.internal_diff(u'old', [b'text\n', b'contents\n'],
249
u'new', [b'text\n', b'contents\n'],
227
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
u'new', ['text\n', 'contents\n'],
251
self.assertEqual(b'', output.getvalue())
230
self.assertEqual('', output.getvalue())
253
232
def test_internal_diff_returns_bytes(self):
255
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
256
u'new_\xe5', [b'new_text\n'], output)
257
output.check_types(self, bytes)
259
def test_internal_diff_default_context(self):
261
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
262
b'same_text\n', b'same_text\n', b'old_text\n'],
263
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
264
b'same_text\n', b'same_text\n', b'new_text\n'], output)
265
lines = output.getvalue().splitlines(True)
266
self.check_patch(lines)
267
self.assertEqual([b'--- old\n',
269
b'@@ -3,4 +3,4 @@\n',
278
def test_internal_diff_no_context(self):
280
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
281
b'same_text\n', b'same_text\n', b'old_text\n'],
282
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
283
b'same_text\n', b'same_text\n', b'new_text\n'], output,
285
lines = output.getvalue().splitlines(True)
286
self.check_patch(lines)
287
self.assertEqual([b'--- old\n',
289
b'@@ -6,1 +6,1 @@\n',
295
def test_internal_diff_more_context(self):
297
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
298
b'same_text\n', b'same_text\n', b'old_text\n'],
299
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
300
b'same_text\n', b'same_text\n', b'new_text\n'], output,
302
lines = output.getvalue().splitlines(True)
303
self.check_patch(lines)
304
self.assertEqual([b'--- old\n',
306
b'@@ -2,5 +2,5 @@\n',
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')
317
241
class TestDiffFiles(tests.TestCaseInTempDir):
319
243
def test_external_diff_binary(self):
320
244
"""The output when using external diff should use diff's i18n error"""
321
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
322
self.overrideEnv(lang, 'C')
323
245
# Make sure external_diff doesn't fail in the current LANG
324
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
246
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
326
248
cmd = ['diff', '-u', '--binary', 'old', 'new']
327
with open('old', 'wb') as f:
328
f.write(b'\x00foobar\n')
329
with open('new', 'wb') as f:
330
f.write(b'foo\x00bar\n')
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('foo\x00bar\n')
331
251
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
332
stdin=subprocess.PIPE)
252
stdin=subprocess.PIPE)
333
253
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
334
256
# We should output whatever diff tells us, plus a trailing newline
335
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
338
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
340
if working_tree is not None:
341
extra_trees = (working_tree,)
344
diff.show_diff_trees(tree1, tree2, output,
345
specific_files=specific_files,
346
extra_trees=extra_trees, old_label='old/',
348
return output.getvalue()
351
class TestDiffDates(tests.TestCaseWithTransport):
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):
354
279
super(TestDiffDates, self).setUp()
355
280
self.wt = self.make_branch_and_tree('.')
356
281
self.b = self.wt.branch
357
282
self.build_tree_contents([
358
('file1', b'file1 contents at rev 1\n'),
359
('file2', b'file2 contents at rev 1\n')
283
('file1', 'file1 contents at rev 1\n'),
284
('file2', 'file2 contents at rev 1\n')
361
286
self.wt.add(['file1', 'file2'])
363
288
message='Revision 1',
364
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
289
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
367
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
292
self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
369
294
message='Revision 2',
370
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
295
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
373
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
298
self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
375
300
message='Revision 3',
376
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
301
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
379
304
self.wt.remove(['file2'])
381
306
message='Revision 4',
382
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
307
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
385
310
self.build_tree_contents([
386
('file1', b'file1 contents in working tree\n')
311
('file1', 'file1 contents in working tree\n')
388
313
# set the date stamps for files in the working tree to known values
389
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
314
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
391
316
def test_diff_rev_tree_working_tree(self):
392
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
317
output = self.get_diff(self.wt.basis_tree(), self.wt)
393
318
# note that the date for old/file1 is from rev 2 rather than from
394
319
# the basis revision (rev 4)
395
self.assertEqualDiff(output, b'''\
320
self.assertEqualDiff(output, '''\
396
321
=== modified file 'file1'
397
322
--- old/file1\t2006-04-02 00:00:00 +0000
398
323
+++ new/file1\t2006-04-05 00:00:00 +0000
468
393
self.wt.add(['dir1', 'dir2'])
469
394
self.wt.rename_one('file1', 'dir1/file1')
470
old_tree = self.b.repository.revision_tree(b'rev-1')
471
new_tree = self.b.repository.revision_tree(b'rev-4')
472
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
473
working_tree=self.wt)
474
self.assertContainsRe(out, b'file1\t')
475
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
476
working_tree=self.wt)
477
self.assertNotContainsRe(out, b'file1\t')
480
class TestShowDiffTrees(tests.TestCaseWithTransport):
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):
481
407
"""Direct tests for show_diff_trees"""
483
409
def test_modified_file(self):
484
410
"""Test when a file is modified."""
485
411
tree = self.make_branch_and_tree('tree')
486
self.build_tree_contents([('tree/file', b'contents\n')])
487
tree.add(['file'], [b'file-id'])
488
tree.commit('one', rev_id=b'rev-1')
412
self.build_tree_contents([('tree/file', 'contents\n')])
413
tree.add(['file'], ['file-id'])
414
tree.commit('one', rev_id='rev-1')
490
self.build_tree_contents([('tree/file', b'new contents\n')])
491
d = get_diff_as_string(tree.basis_tree(), tree)
492
self.assertContainsRe(d, b"=== modified file 'file'\n")
493
self.assertContainsRe(d, b'--- old/file\t')
494
self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
495
self.assertContainsRe(d, b'-contents\n'
496
b'\\+new contents\n')
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'
498
424
def test_modified_file_in_renamed_dir(self):
499
425
"""Test when a file is modified in a renamed directory."""
500
426
tree = self.make_branch_and_tree('tree')
501
427
self.build_tree(['tree/dir/'])
502
self.build_tree_contents([('tree/dir/file', b'contents\n')])
503
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
504
tree.commit('one', rev_id=b'rev-1')
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')
506
432
tree.rename_one('dir', 'other')
507
self.build_tree_contents([('tree/other/file', b'new contents\n')])
508
d = get_diff_as_string(tree.basis_tree(), tree)
509
self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
510
self.assertContainsRe(d, b"=== modified file 'other/file'\n")
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")
511
437
# XXX: This is technically incorrect, because it used to be at another
512
438
# location. What to do?
513
self.assertContainsRe(d, b'--- old/dir/file\t')
514
self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
515
self.assertContainsRe(d, b'-contents\n'
516
b'\\+new contents\n')
439
self.assertContainsRe(d, '--- old/dir/file\t')
440
self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
441
self.assertContainsRe(d, '-contents\n'
518
444
def test_renamed_directory(self):
519
445
"""Test when only a directory is only renamed."""
520
446
tree = self.make_branch_and_tree('tree')
521
447
self.build_tree(['tree/dir/'])
522
self.build_tree_contents([('tree/dir/file', b'contents\n')])
523
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
524
tree.commit('one', rev_id=b'rev-1')
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')
526
452
tree.rename_one('dir', 'newdir')
527
d = get_diff_as_string(tree.basis_tree(), tree)
453
d = self.get_diff(tree.basis_tree(), tree)
528
454
# Renaming a directory should be a single "you renamed this dir" even
529
455
# when there are files inside.
530
self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
456
self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
532
458
def test_renamed_file(self):
533
459
"""Test when a file is only renamed."""
534
460
tree = self.make_branch_and_tree('tree')
535
self.build_tree_contents([('tree/file', b'contents\n')])
536
tree.add(['file'], [b'file-id'])
537
tree.commit('one', rev_id=b'rev-1')
461
self.build_tree_contents([('tree/file', 'contents\n')])
462
tree.add(['file'], ['file-id'])
463
tree.commit('one', rev_id='rev-1')
539
465
tree.rename_one('file', 'newname')
540
d = get_diff_as_string(tree.basis_tree(), tree)
541
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
466
d = self.get_diff(tree.basis_tree(), tree)
467
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
542
468
# We shouldn't have a --- or +++ line, because there is no content
544
self.assertNotContainsRe(d, b'---')
470
self.assertNotContainsRe(d, '---')
546
472
def test_renamed_and_modified_file(self):
547
473
"""Test when a file is only renamed."""
548
474
tree = self.make_branch_and_tree('tree')
549
self.build_tree_contents([('tree/file', b'contents\n')])
550
tree.add(['file'], [b'file-id'])
551
tree.commit('one', rev_id=b'rev-1')
475
self.build_tree_contents([('tree/file', 'contents\n')])
476
tree.add(['file'], ['file-id'])
477
tree.commit('one', rev_id='rev-1')
553
479
tree.rename_one('file', 'newname')
554
self.build_tree_contents([('tree/newname', b'new contents\n')])
555
d = get_diff_as_string(tree.basis_tree(), tree)
556
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
557
self.assertContainsRe(d, b'--- old/file\t')
558
self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
559
self.assertContainsRe(d, b'-contents\n'
560
b'\\+new contents\n')
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'
562
489
def test_internal_diff_exec_property(self):
563
490
tree = self.make_branch_and_tree('tree')
565
tt = tree.get_transform()
566
tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
567
tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
568
tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
569
tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
570
tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
571
tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
492
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)
573
tree.commit('one', rev_id=b'rev-1')
500
tree.commit('one', rev_id='rev-1')
575
tt = tree.get_transform()
576
tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
577
tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
578
tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
579
tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
502
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'))
581
508
tree.rename_one('c', 'new-c')
582
509
tree.rename_one('d', 'new-d')
584
d = get_diff_as_string(tree.basis_tree(), tree)
586
self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
588
self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
590
self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
592
self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
594
self.assertNotContainsRe(d, br"file 'e'")
595
self.assertNotContainsRe(d, br"file 'f'")
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'")
597
525
def test_binary_unicode_filenames(self):
598
526
"""Test that contents of files are *not* encoded in UTF-8 when there
599
527
is a binary file in the diff.
601
529
# See https://bugs.launchpad.net/bugs/110092.
602
self.requireFeature(features.UnicodeFilenameFeature)
530
self.requireFeature(tests.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
604
534
tree = self.make_branch_and_tree('tree')
605
535
alpha, omega = u'\u03b1', u'\u03c9'
606
536
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
607
537
self.build_tree_contents(
608
[('tree/' + alpha, b'\0'),
538
[('tree/' + alpha, chr(0)),
609
539
('tree/' + omega,
610
(b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
611
tree.add([alpha], [b'file-id'])
612
tree.add([omega], [b'file-id-2'])
613
diff_content = StubO()
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()
614
544
diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
615
diff_content.check_types(self, bytes)
616
d = b''.join(diff_content.write_record)
617
self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
618
self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
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"
619
548
% (alpha_utf8, alpha_utf8))
620
self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
621
self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
622
self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_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,))
624
553
def test_unicode_filename(self):
625
554
"""Test when the filename are unicode."""
626
self.requireFeature(features.UnicodeFilenameFeature)
555
self.requireFeature(tests.UnicodeFilenameFeature)
628
557
alpha, omega = u'\u03b1', u'\u03c9'
629
558
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
631
560
tree = self.make_branch_and_tree('tree')
632
self.build_tree_contents([('tree/ren_' + alpha, b'contents\n')])
633
tree.add(['ren_' + alpha], [b'file-id-2'])
634
self.build_tree_contents([('tree/del_' + alpha, b'contents\n')])
635
tree.add(['del_' + alpha], [b'file-id-3'])
636
self.build_tree_contents([('tree/mod_' + alpha, b'contents\n')])
637
tree.add(['mod_' + alpha], [b'file-id-4'])
639
tree.commit('one', rev_id=b'rev-1')
641
tree.rename_one('ren_' + alpha, 'ren_' + omega)
642
tree.remove('del_' + alpha)
643
self.build_tree_contents([('tree/add_' + alpha, b'contents\n')])
644
tree.add(['add_' + alpha], [b'file-id'])
645
self.build_tree_contents([('tree/mod_' + alpha, b'contents_mod\n')])
647
d = get_diff_as_string(tree.basis_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)
648
577
self.assertContainsRe(d,
649
b"=== renamed file 'ren_%s' => 'ren_%s'\n" % (autf8, outf8))
650
self.assertContainsRe(d, b"=== added file 'add_%s'" % autf8)
651
self.assertContainsRe(d, b"=== modified file 'mod_%s'" % autf8)
652
self.assertContainsRe(d, b"=== removed file 'del_%s'" % autf8)
654
def test_unicode_filename_path_encoding(self):
655
"""Test for bug #382699: unicode filenames on Windows should be shown
658
self.requireFeature(features.UnicodeFilenameFeature)
659
# The word 'test' in Russian
660
_russian_test = u'\u0422\u0435\u0441\u0442'
661
directory = _russian_test + u'/'
662
test_txt = _russian_test + u'.txt'
663
u1234 = u'\u1234.txt'
665
tree = self.make_branch_and_tree('.')
666
self.build_tree_contents([
667
(test_txt, b'foo\n'),
671
tree.add([test_txt, u1234, directory])
674
diff.show_diff_trees(tree.basis_tree(), tree, sio,
675
path_encoding='cp1251')
677
output = subst_dates(sio.getvalue())
679
=== added directory '%(directory)s'
680
=== added file '%(test_txt)s'
681
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
682
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
686
=== added file '?.txt'
687
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
688
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
692
''' % {b'directory': _russian_test.encode('cp1251'),
693
b'test_txt': test_txt.encode('cp1251'),
695
self.assertEqualDiff(output, shouldbe)
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)
698
584
class DiffWasIs(diff.DiffPath):
700
def diff(self, old_path, new_path, old_kind, new_kind):
701
self.to_file.write(b'was: ')
702
self.to_file.write(self.old_tree.get_file(old_path).read())
703
self.to_file.write(b'is: ')
704
self.to_file.write(self.new_tree.get_file(new_path).read())
586
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())
707
594
class TestDiffTree(tests.TestCaseWithTransport):
714
601
self.new_tree = self.make_branch_and_tree('new-tree')
715
602
self.new_tree.lock_write()
716
603
self.addCleanup(self.new_tree.unlock)
717
self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
604
self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
719
606
def test_diff_text(self):
720
607
self.build_tree_contents([('old-tree/olddir/',),
721
('old-tree/olddir/oldfile', b'old\n')])
608
('old-tree/olddir/oldfile', 'old\n')])
722
609
self.old_tree.add('olddir')
723
self.old_tree.add('olddir/oldfile', b'file-id')
610
self.old_tree.add('olddir/oldfile', 'file-id')
724
611
self.build_tree_contents([('new-tree/newdir/',),
725
('new-tree/newdir/newfile', b'new\n')])
612
('new-tree/newdir/newfile', 'new\n')])
726
613
self.new_tree.add('newdir')
727
self.new_tree.add('newdir/newfile', b'file-id')
728
differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
729
differ.diff_text('olddir/oldfile', None, 'old label', 'new label')
731
b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
732
differ.to_file.getvalue())
733
differ.to_file.seek(0)
734
differ.diff_text(None, 'newdir/newfile',
735
'old label', 'new label')
737
b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
738
differ.to_file.getvalue())
739
differ.to_file.seek(0)
740
differ.diff_text('olddir/oldfile', 'newdir/newfile',
741
'old label', 'new label')
743
b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
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',
744
629
differ.to_file.getvalue())
746
631
def test_diff_deletion(self):
747
self.build_tree_contents([('old-tree/file', b'contents'),
748
('new-tree/file', b'contents')])
749
self.old_tree.add('file', b'file-id')
750
self.new_tree.add('file', b'file-id')
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')
751
636
os.unlink('new-tree/file')
752
637
self.differ.show_diff(None)
753
self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
638
self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
755
640
def test_diff_creation(self):
756
self.build_tree_contents([('old-tree/file', b'contents'),
757
('new-tree/file', b'contents')])
758
self.old_tree.add('file', b'file-id')
759
self.new_tree.add('file', b'file-id')
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')
760
645
os.unlink('old-tree/file')
761
646
self.differ.show_diff(None)
762
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
764
649
def test_diff_symlink(self):
765
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
766
651
differ.diff_symlink('old target', None)
767
self.assertEqual(b"=== target was 'old target'\n",
652
self.assertEqual("=== target was 'old target'\n",
768
653
differ.to_file.getvalue())
770
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
771
656
differ.diff_symlink(None, 'new target')
772
self.assertEqual(b"=== target is 'new target'\n",
657
self.assertEqual("=== target is 'new target'\n",
773
658
differ.to_file.getvalue())
775
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
776
661
differ.diff_symlink('old target', 'new target')
777
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
662
self.assertEqual("=== target changed 'old target' => 'new target'\n",
778
663
differ.to_file.getvalue())
780
665
def test_diff(self):
781
666
self.build_tree_contents([('old-tree/olddir/',),
782
('old-tree/olddir/oldfile', b'old\n')])
667
('old-tree/olddir/oldfile', 'old\n')])
783
668
self.old_tree.add('olddir')
784
self.old_tree.add('olddir/oldfile', b'file-id')
669
self.old_tree.add('olddir/oldfile', 'file-id')
785
670
self.build_tree_contents([('new-tree/newdir/',),
786
('new-tree/newdir/newfile', b'new\n')])
671
('new-tree/newdir/newfile', 'new\n')])
787
672
self.new_tree.add('newdir')
788
self.new_tree.add('newdir/newfile', b'file-id')
789
self.differ.diff('olddir/oldfile', 'newdir/newfile')
673
self.new_tree.add('newdir/newfile', 'file-id')
674
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
790
675
self.assertContainsRe(
791
676
self.differ.to_file.getvalue(),
792
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
793
br' \@\@\n-old\n\+new\n\n')
677
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
' \@\@\n-old\n\+new\n\n')
795
680
def test_diff_kind_change(self):
796
self.requireFeature(features.SymlinkFeature)
681
self.requireFeature(tests.SymlinkFeature)
797
682
self.build_tree_contents([('old-tree/olddir/',),
798
('old-tree/olddir/oldfile', b'old\n')])
683
('old-tree/olddir/oldfile', 'old\n')])
799
684
self.old_tree.add('olddir')
800
self.old_tree.add('olddir/oldfile', b'file-id')
685
self.old_tree.add('olddir/oldfile', 'file-id')
801
686
self.build_tree(['new-tree/newdir/'])
802
687
os.symlink('new', 'new-tree/newdir/newfile')
803
688
self.new_tree.add('newdir')
804
self.new_tree.add('newdir/newfile', b'file-id')
805
self.differ.diff('olddir/oldfile', 'newdir/newfile')
689
self.new_tree.add('newdir/newfile', 'file-id')
690
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
806
691
self.assertContainsRe(
807
692
self.differ.to_file.getvalue(),
808
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
693
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
810
695
self.assertContainsRe(self.differ.to_file.getvalue(),
811
b"=== target is 'new'\n")
696
"=== target is u'new'\n")
813
698
def test_diff_directory(self):
814
699
self.build_tree(['new-tree/new-dir/'])
815
self.new_tree.add('new-dir', b'new-dir-id')
816
self.differ.diff(None, 'new-dir')
817
self.assertEqual(self.differ.to_file.getvalue(), b'')
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(), '')
819
704
def create_old_new(self):
820
705
self.build_tree_contents([('old-tree/olddir/',),
821
('old-tree/olddir/oldfile', b'old\n')])
706
('old-tree/olddir/oldfile', 'old\n')])
822
707
self.old_tree.add('olddir')
823
self.old_tree.add('olddir/oldfile', b'file-id')
708
self.old_tree.add('olddir/oldfile', 'file-id')
824
709
self.build_tree_contents([('new-tree/newdir/',),
825
('new-tree/newdir/newfile', b'new\n')])
710
('new-tree/newdir/newfile', 'new\n')])
826
711
self.new_tree.add('newdir')
827
self.new_tree.add('newdir/newfile', b'file-id')
712
self.new_tree.add('newdir/newfile', 'file-id')
829
714
def test_register_diff(self):
830
715
self.create_old_new()
831
716
old_diff_factories = diff.DiffTree.diff_factories
832
diff.DiffTree.diff_factories = old_diff_factories[:]
717
diff.DiffTree.diff_factories=old_diff_factories[:]
833
718
diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
835
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
720
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
837
722
diff.DiffTree.diff_factories = old_diff_factories
838
differ.diff('olddir/oldfile', 'newdir/newfile')
723
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
839
724
self.assertNotContainsRe(
840
725
differ.to_file.getvalue(),
841
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
842
br' \@\@\n-old\n\+new\n\n')
726
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
' \@\@\n-old\n\+new\n\n')
843
728
self.assertContainsRe(differ.to_file.getvalue(),
844
b'was: old\nis: new\n')
729
'was: old\nis: new\n')
846
731
def test_extra_factories(self):
847
732
self.create_old_new()
848
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
733
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
849
734
extra_factories=[DiffWasIs.from_diff_tree])
850
differ.diff('olddir/oldfile', 'newdir/newfile')
735
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
851
736
self.assertNotContainsRe(
852
737
differ.to_file.getvalue(),
853
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
854
br' \@\@\n-old\n\+new\n\n')
738
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
' \@\@\n-old\n\+new\n\n')
855
740
self.assertContainsRe(differ.to_file.getvalue(),
856
b'was: old\nis: new\n')
741
'was: old\nis: new\n')
858
743
def test_alphabetical_order(self):
859
744
self.build_tree(['new-tree/a-file'])
862
747
self.old_tree.add('b-file')
863
748
self.differ.show_diff(None)
864
749
self.assertContainsRe(self.differ.to_file.getvalue(),
865
b'.*a-file(.|\n)*b-file')
750
'.*a-file(.|\n)*b-file')
753
class TestPatienceDiffLib(tests.TestCase):
756
super(TestPatienceDiffLib, self).setUp()
757
self._unique_lcs = _patiencediff_py.unique_lcs_py
758
self._recurse_matches = _patiencediff_py.recurse_matches_py
759
self._PatienceSequenceMatcher = \
760
_patiencediff_py.PatienceSequenceMatcher_py
762
def test_diff_unicode_string(self):
763
a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
764
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
sm = self._PatienceSequenceMatcher(None, a, b)
766
mb = sm.get_matching_blocks()
767
self.assertEquals(35, len(mb))
769
def test_unique_lcs(self):
770
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)])
783
def test_recurse_matches(self):
784
def test_one(a, b, matches):
786
self._recurse_matches(
787
a, b, 0, 0, len(a), len(b), test_matches, 10)
788
self.assertEquals(test_matches, matches)
790
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
[(0, 0), (2, 2), (4, 4)])
792
test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
793
[(0, 0), (2, 1), (4, 2)])
794
# Even though 'bc' is not unique globally, and is surrounded by
795
# non-matching lines, we should still match, because they are locally
797
test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
798
(4, 6), (5, 7), (6, 8)])
800
# recurse_matches doesn't match non-unique
801
# lines surrounded by bogus text.
802
# The update has been done in patiencediff.SequenceMatcher instead
804
# This is what it could be
805
#test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
807
# This is what it currently gives:
808
test_one('aBccDe', 'abccde', [(0,0), (5,5)])
810
def assertDiffBlocks(self, a, b, expected_blocks):
811
"""Check that the sequence matcher returns the correct blocks.
813
:param a: A sequence to match
814
:param b: Another sequence to match
815
:param expected_blocks: The expected output, not including the final
816
matching block (len(a), len(b), 0)
818
matcher = self._PatienceSequenceMatcher(None, a, b)
819
blocks = matcher.get_matching_blocks()
821
self.assertEqual((len(a), len(b), 0), last)
822
self.assertEqual(expected_blocks, blocks)
824
def test_matching_blocks(self):
825
# Some basic matching tests
826
self.assertDiffBlocks('', '', [])
827
self.assertDiffBlocks([], [], [])
828
self.assertDiffBlocks('abc', '', [])
829
self.assertDiffBlocks('', 'abc', [])
830
self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
831
self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
832
self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
833
self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
834
self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
835
self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
836
self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
837
# This may check too much, but it checks to see that
838
# a copied block stays attached to the previous section,
840
# difflib would tend to grab the trailing longest match
841
# which would make the diff not look right
842
self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
843
[(0, 0, 6), (6, 11, 10)])
845
# make sure it supports passing in lists
846
self.assertDiffBlocks(
849
'how are you today?\n'],
851
'how are you today?\n'],
852
[(0, 0, 1), (2, 1, 1)])
854
# non unique lines surrounded by non-matching lines
856
self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
858
# But they only need to be locally unique
859
self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
861
# non unique blocks won't be matched
862
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
864
# but locally unique ones will
865
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
866
(5,4,1), (7,5,2), (10,8,1)])
868
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
869
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
870
self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
872
def test_matching_blocks_tuples(self):
873
# Some basic matching tests
874
self.assertDiffBlocks([], [], [])
875
self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
876
self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
877
self.assertDiffBlocks([('a',), ('b',), ('c,')],
878
[('a',), ('b',), ('c,')],
880
self.assertDiffBlocks([('a',), ('b',), ('c,')],
881
[('a',), ('b',), ('d,')],
883
self.assertDiffBlocks([('d',), ('b',), ('c,')],
884
[('a',), ('b',), ('c,')],
886
self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
887
[('a',), ('b',), ('c,')],
889
self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
890
[('a', 'b'), ('c', 'X'), ('e', 'f')],
891
[(0, 0, 1), (2, 2, 1)])
892
self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
893
[('a', 'b'), ('c', 'dX'), ('e', 'f')],
894
[(0, 0, 1), (2, 2, 1)])
896
def test_opcodes(self):
897
def chk_ops(a, b, expected_codes):
898
s = self._PatienceSequenceMatcher(None, a, b)
899
self.assertEquals(expected_codes, s.get_opcodes())
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),
920
chk_ops('abcde', 'abXYZde', [('equal', 0,2, 0,2),
921
('replace', 2,3, 2,5),
924
chk_ops('abde', 'abXYZde', [('equal', 0,2, 0,2),
925
('insert', 2,2, 2,5),
928
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
[('equal', 0,6, 0,6),
930
('insert', 6,6, 6,11),
931
('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),
943
chk_ops('aBccDe', 'abccde',
944
[('equal', 0,1, 0,1),
945
('replace', 1,5, 1,5),
948
chk_ops('aBcDec', 'abcdec',
949
[('equal', 0,1, 0,1),
950
('replace', 1,2, 1,2),
952
('replace', 3,4, 3,4),
955
chk_ops('aBcdEcdFg', 'abcdecdfg',
956
[('equal', 0,1, 0,1),
957
('replace', 1,8, 1,8),
960
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)
972
def test_grouped_opcodes(self):
973
def chk_ops(a, b, expected_codes, n=3):
974
s = self._PatienceSequenceMatcher(None, a, b)
975
self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
979
chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
980
chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
981
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),
989
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
990
[[('equal', 3,6, 3,6),
991
('insert', 6,6, 6,11),
992
('equal', 6,9, 11,14)
994
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
995
[[('equal', 2,6, 2,6),
996
('insert', 6,6, 6,11),
997
('equal', 6,10, 11,15)
999
chk_ops('Xabcdef', 'abcdef',
1000
[[('delete', 0,1, 0,0),
1003
chk_ops('abcdef', 'abcdefX',
1004
[[('equal', 3,6, 3,6),
1005
('insert', 6,6, 6,7)
1009
def test_multiple_ranges(self):
1010
# There was an earlier bug where we used a bad set of ranges,
1011
# this triggers that specific bug, to make sure it doesn't regress
1012
self.assertDiffBlocks('abcdefghijklmnop',
1013
'abcXghiYZQRSTUVWXYZijklmnop',
1014
[(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1016
self.assertDiffBlocks('ABCd efghIjk L',
1017
'AxyzBCn mo pqrstuvwI1 2 L',
1018
[(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1020
# These are rot13 code snippets.
1021
self.assertDiffBlocks('''\
1022
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1024
gnxrf_netf = ['svyr*']
1025
gnxrf_bcgvbaf = ['ab-erphefr']
1027
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1028
sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1030
ercbegre = nqq_ercbegre_ahyy
1032
ercbegre = nqq_ercbegre_cevag
1033
fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
1036
pynff pzq_zxqve(Pbzznaq):
1037
'''.splitlines(True), '''\
1038
trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1040
--qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1043
gnxrf_netf = ['svyr*']
1044
gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
1046
qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
1051
# Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
1052
npgvba = omeyvo.nqq.nqq_npgvba_ahyy
1054
npgvba = omeyvo.nqq.nqq_npgvba_cevag
1056
npgvba = omeyvo.nqq.nqq_npgvba_nqq
1058
npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1060
omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1063
pynff pzq_zxqve(Pbzznaq):
1064
'''.splitlines(True)
1065
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1067
def test_patience_unified_diff(self):
1068
txt_a = ['hello there\n',
1070
'how are you today?\n']
1071
txt_b = ['hello there\n',
1072
'how are you today?\n']
1073
unified_diff = patiencediff.unified_diff
1074
psm = self._PatienceSequenceMatcher
1075
self.assertEquals(['--- \n',
1077
'@@ -1,3 +1,2 @@\n',
1080
' how are you today?\n'
1082
, list(unified_diff(txt_a, txt_b,
1083
sequencematcher=psm)))
1084
txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1085
txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1086
# This is the result with LongestCommonSubstring matching
1087
self.assertEquals(['--- \n',
1089
'@@ -1,6 +1,11 @@\n',
1101
, list(unified_diff(txt_a, txt_b)))
1102
# And the patience diff
1103
self.assertEquals(['--- \n',
1105
'@@ -4,6 +4,11 @@\n',
1118
, list(unified_diff(txt_a, txt_b,
1119
sequencematcher=psm)))
1121
def test_patience_unified_diff_with_dates(self):
1122
txt_a = ['hello there\n',
1124
'how are you today?\n']
1125
txt_b = ['hello there\n',
1126
'how are you today?\n']
1127
unified_diff = patiencediff.unified_diff
1128
psm = self._PatienceSequenceMatcher
1129
self.assertEquals(['--- a\t2008-08-08\n',
1130
'+++ b\t2008-09-09\n',
1131
'@@ -1,3 +1,2 @@\n',
1134
' how are you today?\n'
1136
, list(unified_diff(txt_a, txt_b,
1137
fromfile='a', tofile='b',
1138
fromfiledate='2008-08-08',
1139
tofiledate='2008-09-09',
1140
sequencematcher=psm)))
1143
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1145
_test_needs_features = [compiled_patiencediff_feature]
1148
super(TestPatienceDiffLib_c, self).setUp()
1149
from bzrlib import _patiencediff_c
1150
self._unique_lcs = _patiencediff_c.unique_lcs_c
1151
self._recurse_matches = _patiencediff_c.recurse_matches_c
1152
self._PatienceSequenceMatcher = \
1153
_patiencediff_c.PatienceSequenceMatcher_c
1155
def test_unhashable(self):
1156
"""We should get a proper exception here."""
1157
# We need to be able to hash items in the sequence, lists are
1158
# unhashable, and thus cannot be diffed
1159
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1161
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1162
None, ['valid', []], [])
1163
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1164
None, ['valid'], [[]])
1165
e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1166
None, ['valid'], ['valid', []])
1169
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1172
super(TestPatienceDiffLibFiles, self).setUp()
1173
self._PatienceSequenceMatcher = \
1174
_patiencediff_py.PatienceSequenceMatcher_py
1176
def test_patience_unified_diff_files(self):
1177
txt_a = ['hello there\n',
1179
'how are you today?\n']
1180
txt_b = ['hello there\n',
1181
'how are you today?\n']
1182
open('a1', 'wb').writelines(txt_a)
1183
open('b1', 'wb').writelines(txt_b)
1185
unified_diff_files = patiencediff.unified_diff_files
1186
psm = self._PatienceSequenceMatcher
1187
self.assertEquals(['--- a1\n',
1189
'@@ -1,3 +1,2 @@\n',
1192
' how are you today?\n',
1194
, list(unified_diff_files('a1', 'b1',
1195
sequencematcher=psm)))
1197
txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1198
txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1199
open('a2', 'wb').writelines(txt_a)
1200
open('b2', 'wb').writelines(txt_b)
1202
# This is the result with LongestCommonSubstring matching
1203
self.assertEquals(['--- a2\n',
1205
'@@ -1,6 +1,11 @@\n',
1217
, list(unified_diff_files('a2', 'b2')))
1219
# And the patience diff
1220
self.assertEquals(['--- a2\n',
1222
'@@ -4,6 +4,11 @@\n',
1235
, list(unified_diff_files('a2', 'b2',
1236
sequencematcher=psm)))
1239
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1241
_test_needs_features = [compiled_patiencediff_feature]
1244
super(TestPatienceDiffLibFiles_c, self).setUp()
1245
from bzrlib import _patiencediff_c
1246
self._PatienceSequenceMatcher = \
1247
_patiencediff_c.PatienceSequenceMatcher_c
1250
class TestUsingCompiledIfAvailable(tests.TestCase):
1252
def test_PatienceSequenceMatcher(self):
1253
if compiled_patiencediff_feature.available():
1254
from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1255
self.assertIs(PatienceSequenceMatcher_c,
1256
patiencediff.PatienceSequenceMatcher)
1258
from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1259
self.assertIs(PatienceSequenceMatcher_py,
1260
patiencediff.PatienceSequenceMatcher)
1262
def test_unique_lcs(self):
1263
if compiled_patiencediff_feature.available():
1264
from bzrlib._patiencediff_c import unique_lcs_c
1265
self.assertIs(unique_lcs_c,
1266
patiencediff.unique_lcs)
1268
from bzrlib._patiencediff_py import unique_lcs_py
1269
self.assertIs(unique_lcs_py,
1270
patiencediff.unique_lcs)
1272
def test_recurse_matches(self):
1273
if compiled_patiencediff_feature.available():
1274
from bzrlib._patiencediff_c import recurse_matches_c
1275
self.assertIs(recurse_matches_c,
1276
patiencediff.recurse_matches)
1278
from bzrlib._patiencediff_py import recurse_matches_py
1279
self.assertIs(recurse_matches_py,
1280
patiencediff.recurse_matches)
868
1283
class TestDiffFromTool(tests.TestCaseWithTransport):
870
1285
def test_from_string(self):
871
diff_obj = diff.DiffFromTool.from_string(
872
['diff', '{old_path}', '{new_path}'],
874
self.addCleanup(diff_obj.finish)
875
self.assertEqual(['diff', '{old_path}', '{new_path}'],
876
diff_obj.command_template)
878
def test_from_string_no_paths(self):
879
diff_obj = diff.DiffFromTool.from_string(
880
['diff', "-u5"], None, None, None)
881
self.addCleanup(diff_obj.finish)
882
self.assertEqual(['diff', '-u5'],
883
diff_obj.command_template)
884
self.assertEqual(['diff', '-u5', 'old-path', 'new-path'],
885
diff_obj._get_command('old-path', 'new-path'))
1286
diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1287
self.addCleanup(diff_obj.finish)
1288
self.assertEqual(['diff', '@old_path', '@new_path'],
1289
diff_obj.command_template)
887
1291
def test_from_string_u5(self):
888
diff_obj = diff.DiffFromTool.from_string(
889
['diff', "-u 5", '{old_path}', '{new_path}'], None, None, None)
1292
diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
890
1294
self.addCleanup(diff_obj.finish)
891
self.assertEqual(['diff', '-u 5', '{old_path}', '{new_path}'],
1295
self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
892
1296
diff_obj.command_template)
893
1297
self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
894
1298
diff_obj._get_command('old-path', 'new-path'))
896
1300
def test_from_string_path_with_backslashes(self):
897
self.requireFeature(features.backslashdir_feature)
898
tool = ['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}']
1301
self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
1302
tool = 'C:\\Tools\\Diff.exe'
899
1303
diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
900
1304
self.addCleanup(diff_obj.finish)
901
self.assertEqual(['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}'],
1305
self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
902
1306
diff_obj.command_template)
903
1307
self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
904
1308
diff_obj._get_command('old-path', 'new-path'))
906
1310
def test_execute(self):
908
diff_obj = diff.DiffFromTool([sys.executable, '-c',
909
'print("{old_path} {new_path}")'],
1312
diff_obj = diff.DiffFromTool(['python', '-c',
1313
'print "@old_path @new_path"'],
910
1314
None, None, output)
911
1315
self.addCleanup(diff_obj.finish)
912
1316
diff_obj._execute('old', 'new')
913
self.assertEqual(output.getvalue().rstrip(), b'old new')
1317
self.assertEqual(output.getvalue().rstrip(), 'old new')
915
def test_execute_missing(self):
1319
def test_excute_missing(self):
916
1320
diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
917
1321
None, None, None)
918
1322
self.addCleanup(diff_obj.finish)
952
1356
self.assertContainsRe(result.replace('\r\n', '\n'), regex)
954
1358
def test_prepare_files(self):
956
1360
tree = self.make_branch_and_tree('tree')
957
self.build_tree_contents([('tree/oldname', b'oldcontent')])
958
self.build_tree_contents([('tree/oldname2', b'oldcontent2')])
959
tree.add('oldname', b'file-id')
960
tree.add('oldname2', b'file2-id')
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')
961
1365
# Earliest allowable date on FAT32 filesystems is 1980-01-01
962
1366
tree.commit('old tree', timestamp=315532800)
963
1367
tree.rename_one('oldname', 'newname')
964
1368
tree.rename_one('oldname2', 'newname2')
965
self.build_tree_contents([('tree/newname', b'newcontent')])
966
self.build_tree_contents([('tree/newname2', b'newcontent2')])
1369
self.build_tree_contents([('tree/newname', 'newcontent')])
1370
self.build_tree_contents([('tree/newname2', 'newcontent2')])
967
1371
old_tree = tree.basis_tree()
968
1372
old_tree.lock_read()
969
1373
self.addCleanup(old_tree.unlock)
970
1374
tree.lock_read()
971
1375
self.addCleanup(tree.unlock)
972
diff_obj = diff.DiffFromTool([sys.executable, '-c',
973
'print "{old_path} {new_path}"'],
1376
diff_obj = diff.DiffFromTool(['python', '-c',
1377
'print "@old_path @new_path"'],
974
1378
old_tree, tree, output)
975
1379
self.addCleanup(diff_obj.finish)
976
self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
977
old_path, new_path = diff_obj._prepare_files(
978
'oldname', 'newname')
1380
self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1381
old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
979
1383
self.assertContainsRe(old_path, 'old/oldname$')
980
1384
self.assertEqual(315532800, os.stat(old_path).st_mtime)
981
1385
self.assertContainsRe(new_path, 'tree/newname$')
982
self.assertFileEqual(b'oldcontent', old_path)
983
self.assertFileEqual(b'newcontent', new_path)
1386
self.assertFileEqual('oldcontent', old_path)
1387
self.assertFileEqual('newcontent', new_path)
984
1388
if osutils.host_os_dereferences_symlinks():
985
1389
self.assertTrue(os.path.samefile('tree/newname', new_path))
986
1390
# make sure we can create files with the same parent directories
987
diff_obj._prepare_files('oldname2', 'newname2')
990
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
992
def test_encodable_filename(self):
993
# Just checks file path for external diff tool.
994
# We cannot change CPython's internal encoding used by os.exec*.
995
diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
997
for _, scenario in EncodingAdapter.encoding_scenarios:
998
encoding = scenario['encoding']
999
dirname = scenario['info']['directory']
1000
filename = scenario['info']['filename']
1002
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1003
relpath = dirname + u'/' + filename
1004
fullpath = diffobj._safe_filename('safe', relpath)
1005
self.assertEqual(fullpath,
1006
fullpath.encode(encoding).decode(encoding))
1007
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1009
def test_unencodable_filename(self):
1010
diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
1012
for _, scenario in EncodingAdapter.encoding_scenarios:
1013
encoding = scenario['encoding']
1014
dirname = scenario['info']['directory']
1015
filename = scenario['info']['filename']
1017
if encoding == 'iso-8859-1':
1018
encoding = 'iso-8859-2'
1020
encoding = 'iso-8859-1'
1022
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1023
relpath = dirname + u'/' + filename
1024
fullpath = diffobj._safe_filename('safe', relpath)
1025
self.assertEqual(fullpath,
1026
fullpath.encode(encoding).decode(encoding))
1027
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1391
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1030
1394
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1032
1396
def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1033
"""Call get_trees_and_branches_to_diff_locked."""
1034
exit_stack = cleanup.ExitStack()
1035
self.addCleanup(exit_stack.close)
1397
"""Call get_trees_and_branches_to_diff_locked. Overridden by
1398
TestGetTreesAndBranchesToDiff.
1036
1400
return diff.get_trees_and_branches_to_diff_locked(
1037
path_list, revision_specs, old_url, new_url, exit_stack)
1401
path_list, revision_specs, old_url, new_url, self.addCleanup)
1039
1403
def test_basic(self):
1040
1404
tree = self.make_branch_and_tree('tree')