74
"""Simple file-like object that allows writes with any type and records."""
77
self.write_record = []
79
def write(self, data):
80
self.write_record.append(data)
82
def check_types(self, testcase, expected_type):
84
any(not isinstance(o, expected_type) for o in self.write_record),
85
"Not all writes of type %s: %r" % (
86
expected_type.__name__, self.write_record))
89
class TestDiffOptions(tests.TestCase):
91
def test_unified_added(self):
92
"""Check for default style '-u' only if no other style specified
95
# Verify that style defaults to unified, id est '-u' appended
96
# to option list, in the absence of an alternative style.
97
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
100
class TestDiffOptionsScenarios(tests.TestCase):
102
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
103
style = None # Set by load_tests_apply_scenarios from scenarios
105
def test_unified_not_added(self):
106
# Verify that for all valid style options, '-u' is not
107
# appended to option list.
108
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
109
self.assertEqual(["%s" % (self.style,)], ret_opts)
83
112
class TestDiff(tests.TestCase):
85
114
def test_add_nl(self):
86
115
"""diff generates a valid diff for patches that add a newline"""
87
lines = udiff_lines(['boo'], ['boo\n'])
116
lines = udiff_lines([b'boo'], [b'boo\n'])
88
117
self.check_patch(lines)
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
90
## "expected no-nl, got %r" % lines[4]
118
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
119
## "expected no-nl, got %r" % lines[4]
92
121
def test_add_nl_2(self):
93
122
"""diff generates a valid diff for patches that change last line and
96
lines = udiff_lines(['boo'], ['goo\n'])
125
lines = udiff_lines([b'boo'], [b'goo\n'])
97
126
self.check_patch(lines)
98
self.assertEquals(lines[4], '\\ No newline at end of file\n')
99
## "expected no-nl, got %r" % lines[4]
127
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
128
## "expected no-nl, got %r" % lines[4]
101
130
def test_remove_nl(self):
102
131
"""diff generates a valid diff for patches that change last line and
105
lines = udiff_lines(['boo\n'], ['boo'])
134
lines = udiff_lines([b'boo\n'], [b'boo'])
106
135
self.check_patch(lines)
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
108
## "expected no-nl, got %r" % lines[5]
136
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
137
## "expected no-nl, got %r" % lines[5]
110
139
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)
140
self.assertTrue(len(lines) > 1)
141
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
142
self.assertTrue(lines[0].startswith(b'---'))
143
## 'No orig line for patch:\n%s' % "".join(lines)
144
self.assertTrue(lines[1].startswith(b'+++'))
145
## 'No mod line for patch:\n%s' % "".join(lines)
146
self.assertTrue(len(lines) > 2)
147
## "No hunks for patch:\n%s" % "".join(lines)
148
self.assertTrue(lines[2].startswith(b'@@'))
149
## "No hunk header for patch:\n%s" % "".join(lines)
150
self.assertTrue(b'@@' in lines[2][2:])
151
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
153
def test_binary_lines(self):
126
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
155
uni_lines = [1023 * b'a' + b'\x00']
156
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
157
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
udiff_lines(uni_lines , empty, allow_binary=True)
158
udiff_lines(uni_lines, empty, allow_binary=True)
130
159
udiff_lines(empty, uni_lines, allow_binary=True)
132
161
def test_external_diff(self):
133
lines = external_udiff_lines(['boo\n'], ['goo\n'])
162
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
134
163
self.check_patch(lines)
135
self.assertEqual('\n', lines[-1])
164
self.assertEqual(b'\n', lines[-1])
137
166
def test_external_diff_no_fileno(self):
138
167
# Make sure that we can handle not having a fileno, even
139
168
# if the diff is large
140
lines = external_udiff_lines(['boo\n']*10000,
169
lines = external_udiff_lines([b'boo\n'] * 10000,
142
171
use_stringio=True)
143
172
self.check_patch(lines)
145
174
def test_external_diff_binary_lang_c(self):
147
175
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)
176
self.overrideEnv(lang, 'C')
177
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
178
# Older versions of diffutils say "Binary files", newer
179
# versions just say "Files".
180
self.assertContainsRe(
181
lines[0], b'(Binary f|F)iles old and new differ\n')
182
self.assertEqual(lines[1:], [b'\n'])
160
184
def test_no_external_diff(self):
161
185
"""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
186
# Make sure no 'diff' command is available
187
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
188
self.overrideEnv('PATH', '')
189
self.assertRaises(errors.NoDiff, diff.external_diff,
190
b'old', [b'boo\n'], b'new', [b'goo\n'],
191
BytesIO(), diff_opts=['-u'])
172
193
def test_internal_diff_default(self):
173
194
# Default internal diff encoding is utf8
175
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
u'new_\xe5', ['new_text\n'], output)
196
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
197
u'new_\xe5', [b'new_text\n'], output)
177
198
lines = output.getvalue().splitlines(True)
178
199
self.check_patch(lines)
179
self.assertEquals(['--- old_\xc2\xb5\n',
180
'+++ new_\xc3\xa5\n',
200
self.assertEqual([b'--- old_\xc2\xb5\n',
201
b'+++ new_\xc3\xa5\n',
202
b'@@ -1,1 +1,1 @@\n',
188
208
def test_internal_diff_utf8(self):
190
diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
u'new_\xe5', ['new_text\n'], output,
210
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
211
u'new_\xe5', [b'new_text\n'], output,
192
212
path_encoding='utf8')
193
213
lines = output.getvalue().splitlines(True)
194
214
self.check_patch(lines)
195
self.assertEquals(['--- old_\xc2\xb5\n',
196
'+++ new_\xc3\xa5\n',
215
self.assertEqual([b'--- old_\xc2\xb5\n',
216
b'+++ new_\xc3\xa5\n',
217
b'@@ -1,1 +1,1 @@\n',
204
223
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,
225
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
226
u'new_\xe5', [b'new_text\n'], output,
208
227
path_encoding='iso-8859-1')
209
228
lines = output.getvalue().splitlines(True)
210
229
self.check_patch(lines)
211
self.assertEquals(['--- old_\xb5\n',
230
self.assertEqual([b'--- old_\xb5\n',
232
b'@@ -1,1 +1,1 @@\n',
220
238
def test_internal_diff_no_content(self):
222
240
diff.internal_diff(u'old', [], u'new', [], output)
223
self.assertEqual('', output.getvalue())
241
self.assertEqual(b'', output.getvalue())
225
243
def test_internal_diff_no_changes(self):
227
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
u'new', ['text\n', 'contents\n'],
245
diff.internal_diff(u'old', [b'text\n', b'contents\n'],
246
u'new', [b'text\n', b'contents\n'],
230
self.assertEqual('', output.getvalue())
248
self.assertEqual(b'', output.getvalue())
232
250
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')
252
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
253
u'new_\xe5', [b'new_text\n'], output)
254
output.check_types(self, bytes)
256
def test_internal_diff_default_context(self):
258
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
259
b'same_text\n', b'same_text\n', b'old_text\n'],
260
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
261
b'same_text\n', b'same_text\n', b'new_text\n'], output)
262
lines = output.getvalue().splitlines(True)
263
self.check_patch(lines)
264
self.assertEqual([b'--- old\n',
266
b'@@ -3,4 +3,4 @@\n',
275
def test_internal_diff_no_context(self):
277
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
278
b'same_text\n', b'same_text\n', b'old_text\n'],
279
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
280
b'same_text\n', b'same_text\n', b'new_text\n'], output,
282
lines = output.getvalue().splitlines(True)
283
self.check_patch(lines)
284
self.assertEqual([b'--- old\n',
286
b'@@ -6,1 +6,1 @@\n',
292
def test_internal_diff_more_context(self):
294
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
295
b'same_text\n', b'same_text\n', b'old_text\n'],
296
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
297
b'same_text\n', b'same_text\n', b'new_text\n'], output,
299
lines = output.getvalue().splitlines(True)
300
self.check_patch(lines)
301
self.assertEqual([b'--- old\n',
303
b'@@ -2,5 +2,5 @@\n',
241
314
class TestDiffFiles(tests.TestCaseInTempDir):
243
316
def test_external_diff_binary(self):
244
317
"""The output when using external diff should use diff's i18n error"""
318
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
319
self.overrideEnv(lang, 'C')
245
320
# Make sure external_diff doesn't fail in the current LANG
246
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
321
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
248
323
cmd = ['diff', '-u', '--binary', 'old', 'new']
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('foo\x00bar\n')
324
with open('old', 'wb') as f:
325
f.write(b'\x00foobar\n')
326
with open('new', 'wb') as f:
327
f.write(b'foo\x00bar\n')
251
328
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
stdin=subprocess.PIPE)
329
stdin=subprocess.PIPE)
253
330
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
256
331
# 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):
332
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
335
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
337
if working_tree is not None:
338
extra_trees = (working_tree,)
341
diff.show_diff_trees(tree1, tree2, output,
342
specific_files=specific_files,
343
extra_trees=extra_trees, old_label='old/',
345
return output.getvalue()
348
class TestDiffDates(tests.TestCaseWithTransport):
279
351
super(TestDiffDates, self).setUp()
280
352
self.wt = self.make_branch_and_tree('.')
281
353
self.b = self.wt.branch
282
354
self.build_tree_contents([
283
('file1', 'file1 contents at rev 1\n'),
284
('file2', 'file2 contents at rev 1\n')
355
('file1', b'file1 contents at rev 1\n'),
356
('file2', b'file2 contents at rev 1\n')
286
358
self.wt.add(['file1', 'file2'])
288
360
message='Revision 1',
289
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
361
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
292
self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
364
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
294
366
message='Revision 2',
295
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
367
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
298
self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
370
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
300
372
message='Revision 3',
301
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
373
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
304
376
self.wt.remove(['file2'])
306
378
message='Revision 4',
307
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
379
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
310
382
self.build_tree_contents([
311
('file1', 'file1 contents in working tree\n')
383
('file1', b'file1 contents in working tree\n')
313
385
# 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
386
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
316
388
def test_diff_rev_tree_working_tree(self):
317
output = self.get_diff(self.wt.basis_tree(), self.wt)
389
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
390
# note that the date for old/file1 is from rev 2 rather than from
319
391
# the basis revision (rev 4)
320
self.assertEqualDiff(output, '''\
392
self.assertEqualDiff(output, b'''\
321
393
=== modified file 'file1'
322
394
--- old/file1\t2006-04-02 00:00:00 +0000
323
395
+++ new/file1\t2006-04-05 00:00:00 +0000
393
465
self.wt.add(['dir1', 'dir2'])
394
466
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):
467
old_tree = self.b.repository.revision_tree(b'rev-1')
468
new_tree = self.b.repository.revision_tree(b'rev-4')
469
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
470
working_tree=self.wt)
471
self.assertContainsRe(out, b'file1\t')
472
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
473
working_tree=self.wt)
474
self.assertNotContainsRe(out, b'file1\t')
477
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
478
"""Direct tests for show_diff_trees"""
409
480
def test_modified_file(self):
410
481
"""Test when a file is modified."""
411
482
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')
483
self.build_tree_contents([('tree/file', b'contents\n')])
484
tree.add(['file'], [b'file-id'])
485
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'
487
self.build_tree_contents([('tree/file', b'new contents\n')])
488
d = get_diff_as_string(tree.basis_tree(), tree)
489
self.assertContainsRe(d, b"=== modified file 'file'\n")
490
self.assertContainsRe(d, b'--- old/file\t')
491
self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
492
self.assertContainsRe(d, b'-contents\n'
493
b'\\+new contents\n')
424
495
def test_modified_file_in_renamed_dir(self):
425
496
"""Test when a file is modified in a renamed directory."""
426
497
tree = self.make_branch_and_tree('tree')
427
498
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')
499
self.build_tree_contents([('tree/dir/file', b'contents\n')])
500
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
501
tree.commit('one', rev_id=b'rev-1')
432
503
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")
504
self.build_tree_contents([('tree/other/file', b'new contents\n')])
505
d = get_diff_as_string(tree.basis_tree(), tree)
506
self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
507
self.assertContainsRe(d, b"=== modified file 'other/file'\n")
437
508
# XXX: This is technically incorrect, because it used to be at another
438
509
# 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'
510
self.assertContainsRe(d, b'--- old/dir/file\t')
511
self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
512
self.assertContainsRe(d, b'-contents\n'
513
b'\\+new contents\n')
444
515
def test_renamed_directory(self):
445
516
"""Test when only a directory is only renamed."""
446
517
tree = self.make_branch_and_tree('tree')
447
518
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')
519
self.build_tree_contents([('tree/dir/file', b'contents\n')])
520
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
521
tree.commit('one', rev_id=b'rev-1')
452
523
tree.rename_one('dir', 'newdir')
453
d = self.get_diff(tree.basis_tree(), tree)
524
d = get_diff_as_string(tree.basis_tree(), tree)
454
525
# Renaming a directory should be a single "you renamed this dir" even
455
526
# when there are files inside.
456
self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
527
self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
458
529
def test_renamed_file(self):
459
530
"""Test when a file is only renamed."""
460
531
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')
532
self.build_tree_contents([('tree/file', b'contents\n')])
533
tree.add(['file'], [b'file-id'])
534
tree.commit('one', rev_id=b'rev-1')
465
536
tree.rename_one('file', 'newname')
466
d = self.get_diff(tree.basis_tree(), tree)
467
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
537
d = get_diff_as_string(tree.basis_tree(), tree)
538
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
468
539
# We shouldn't have a --- or +++ line, because there is no content
470
self.assertNotContainsRe(d, '---')
541
self.assertNotContainsRe(d, b'---')
472
543
def test_renamed_and_modified_file(self):
473
544
"""Test when a file is only renamed."""
474
545
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')
546
self.build_tree_contents([('tree/file', b'contents\n')])
547
tree.add(['file'], [b'file-id'])
548
tree.commit('one', rev_id=b'rev-1')
479
550
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'
551
self.build_tree_contents([('tree/newname', b'new contents\n')])
552
d = get_diff_as_string(tree.basis_tree(), tree)
553
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
554
self.assertContainsRe(d, b'--- old/file\t')
555
self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
556
self.assertContainsRe(d, b'-contents\n'
557
b'\\+new contents\n')
489
559
def test_internal_diff_exec_property(self):
490
560
tree = self.make_branch_and_tree('tree')
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)
562
tt = tree.transform()
563
tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
564
tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
565
tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
566
tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
567
tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
568
tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
500
tree.commit('one', rev_id='rev-1')
570
tree.commit('one', rev_id=b'rev-1')
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'))
572
tt = tree.transform()
573
tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
574
tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
575
tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
576
tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
508
578
tree.rename_one('c', 'new-c')
509
579
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'")
581
d = get_diff_as_string(tree.basis_tree(), tree)
583
self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
585
self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
587
self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
589
self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
591
self.assertNotContainsRe(d, br"file 'e'")
592
self.assertNotContainsRe(d, br"file 'f'")
525
594
def test_binary_unicode_filenames(self):
526
595
"""Test that contents of files are *not* encoded in UTF-8 when there
527
596
is a binary file in the diff.
529
598
# See https://bugs.launchpad.net/bugs/110092.
530
self.requireFeature(tests.UnicodeFilenameFeature)
599
self.requireFeature(features.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
534
601
tree = self.make_branch_and_tree('tree')
535
602
alpha, omega = u'\u03b1', u'\u03c9'
536
603
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
537
604
self.build_tree_contents(
538
[('tree/' + alpha, chr(0)),
605
[('tree/' + alpha, b'\0'),
539
606
('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()
607
(b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
610
diff_content = StubO()
544
611
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"
612
diff_content.check_types(self, bytes)
613
d = b''.join(diff_content.write_record)
614
self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
615
self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
548
616
% (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,))
617
self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
618
self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
619
self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
553
621
def test_unicode_filename(self):
554
622
"""Test when the filename are unicode."""
555
self.requireFeature(tests.UnicodeFilenameFeature)
623
self.requireFeature(features.UnicodeFilenameFeature)
557
625
alpha, omega = u'\u03b1', u'\u03c9'
558
626
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
560
628
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)
629
self.build_tree_contents([('tree/ren_' + alpha, b'contents\n')])
630
tree.add(['ren_' + alpha], [b'file-id-2'])
631
self.build_tree_contents([('tree/del_' + alpha, b'contents\n')])
632
tree.add(['del_' + alpha], [b'file-id-3'])
633
self.build_tree_contents([('tree/mod_' + alpha, b'contents\n')])
634
tree.add(['mod_' + alpha], [b'file-id-4'])
636
tree.commit('one', rev_id=b'rev-1')
638
tree.rename_one('ren_' + alpha, 'ren_' + omega)
639
tree.remove('del_' + alpha)
640
self.build_tree_contents([('tree/add_' + alpha, b'contents\n')])
641
tree.add(['add_' + alpha], [b'file-id'])
642
self.build_tree_contents([('tree/mod_' + alpha, b'contents_mod\n')])
644
d = get_diff_as_string(tree.basis_tree(), tree)
577
645
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)
646
b"=== renamed file 'ren_%s' => 'ren_%s'\n" % (autf8, outf8))
647
self.assertContainsRe(d, b"=== added file 'add_%s'" % autf8)
648
self.assertContainsRe(d, b"=== modified file 'mod_%s'" % autf8)
649
self.assertContainsRe(d, b"=== removed file 'del_%s'" % autf8)
651
def test_unicode_filename_path_encoding(self):
652
"""Test for bug #382699: unicode filenames on Windows should be shown
655
self.requireFeature(features.UnicodeFilenameFeature)
656
# The word 'test' in Russian
657
_russian_test = u'\u0422\u0435\u0441\u0442'
658
directory = _russian_test + u'/'
659
test_txt = _russian_test + u'.txt'
660
u1234 = u'\u1234.txt'
662
tree = self.make_branch_and_tree('.')
663
self.build_tree_contents([
664
(test_txt, b'foo\n'),
668
tree.add([test_txt, u1234, directory])
671
diff.show_diff_trees(tree.basis_tree(), tree, sio,
672
path_encoding='cp1251')
674
output = subst_dates(sio.getvalue())
676
=== added directory '%(directory)s'
677
=== added file '%(test_txt)s'
678
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
679
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
683
=== added file '?.txt'
684
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
685
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
689
''' % {b'directory': _russian_test.encode('cp1251'),
690
b'test_txt': test_txt.encode('cp1251'),
692
self.assertEqualDiff(output, shouldbe)
584
695
class DiffWasIs(diff.DiffPath):
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())
697
def diff(self, old_path, new_path, old_kind, new_kind):
698
self.to_file.write(b'was: ')
699
self.to_file.write(self.old_tree.get_file(old_path).read())
700
self.to_file.write(b'is: ')
701
self.to_file.write(self.new_tree.get_file(new_path).read())
594
704
class TestDiffTree(tests.TestCaseWithTransport):
601
711
self.new_tree = self.make_branch_and_tree('new-tree')
602
712
self.new_tree.lock_write()
603
713
self.addCleanup(self.new_tree.unlock)
604
self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
714
self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
606
716
def test_diff_text(self):
607
717
self.build_tree_contents([('old-tree/olddir/',),
608
('old-tree/olddir/oldfile', 'old\n')])
718
('old-tree/olddir/oldfile', b'old\n')])
609
719
self.old_tree.add('olddir')
610
self.old_tree.add('olddir/oldfile', 'file-id')
720
self.old_tree.add('olddir/oldfile', b'file-id')
611
721
self.build_tree_contents([('new-tree/newdir/',),
612
('new-tree/newdir/newfile', 'new\n')])
722
('new-tree/newdir/newfile', b'new\n')])
613
723
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',
724
self.new_tree.add('newdir/newfile', b'file-id')
725
differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
726
differ.diff_text('olddir/oldfile', None, 'old label', 'new label')
728
b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
729
differ.to_file.getvalue())
730
differ.to_file.seek(0)
731
differ.diff_text(None, 'newdir/newfile',
732
'old label', 'new label')
734
b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
735
differ.to_file.getvalue())
736
differ.to_file.seek(0)
737
differ.diff_text('olddir/oldfile', 'newdir/newfile',
738
'old label', 'new label')
740
b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
629
741
differ.to_file.getvalue())
631
743
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')
744
self.build_tree_contents([('old-tree/file', b'contents'),
745
('new-tree/file', b'contents')])
746
self.old_tree.add('file', b'file-id')
747
self.new_tree.add('file', b'file-id')
636
748
os.unlink('new-tree/file')
637
749
self.differ.show_diff(None)
638
self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
750
self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
640
752
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')
753
self.build_tree_contents([('old-tree/file', b'contents'),
754
('new-tree/file', b'contents')])
755
self.old_tree.add('file', b'file-id')
756
self.new_tree.add('file', b'file-id')
645
757
os.unlink('old-tree/file')
646
758
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
759
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
649
761
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
762
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
763
differ.diff_symlink('old target', None)
652
self.assertEqual("=== target was 'old target'\n",
764
self.assertEqual(b"=== target was 'old target'\n",
653
765
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
767
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
768
differ.diff_symlink(None, 'new target')
657
self.assertEqual("=== target is 'new target'\n",
769
self.assertEqual(b"=== target is 'new target'\n",
658
770
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
772
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
773
differ.diff_symlink('old target', 'new target')
662
self.assertEqual("=== target changed 'old target' => 'new target'\n",
774
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
663
775
differ.to_file.getvalue())
665
777
def test_diff(self):
666
778
self.build_tree_contents([('old-tree/olddir/',),
667
('old-tree/olddir/oldfile', 'old\n')])
779
('old-tree/olddir/oldfile', b'old\n')])
668
780
self.old_tree.add('olddir')
669
self.old_tree.add('olddir/oldfile', 'file-id')
781
self.old_tree.add('olddir/oldfile', b'file-id')
670
782
self.build_tree_contents([('new-tree/newdir/',),
671
('new-tree/newdir/newfile', 'new\n')])
783
('new-tree/newdir/newfile', b'new\n')])
672
784
self.new_tree.add('newdir')
673
self.new_tree.add('newdir/newfile', 'file-id')
674
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
785
self.new_tree.add('newdir/newfile', b'file-id')
786
self.differ.diff('olddir/oldfile', 'newdir/newfile')
675
787
self.assertContainsRe(
676
788
self.differ.to_file.getvalue(),
677
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
' \@\@\n-old\n\+new\n\n')
789
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
790
br' \@\@\n-old\n\+new\n\n')
680
792
def test_diff_kind_change(self):
681
self.requireFeature(tests.SymlinkFeature)
793
self.requireFeature(features.SymlinkFeature)
682
794
self.build_tree_contents([('old-tree/olddir/',),
683
('old-tree/olddir/oldfile', 'old\n')])
795
('old-tree/olddir/oldfile', b'old\n')])
684
796
self.old_tree.add('olddir')
685
self.old_tree.add('olddir/oldfile', 'file-id')
797
self.old_tree.add('olddir/oldfile', b'file-id')
686
798
self.build_tree(['new-tree/newdir/'])
687
799
os.symlink('new', 'new-tree/newdir/newfile')
688
800
self.new_tree.add('newdir')
689
self.new_tree.add('newdir/newfile', 'file-id')
690
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
801
self.new_tree.add('newdir/newfile', b'file-id')
802
self.differ.diff('olddir/oldfile', 'newdir/newfile')
691
803
self.assertContainsRe(
692
804
self.differ.to_file.getvalue(),
693
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
805
br'--- olddir/oldfile.*\n'
806
br'\+\+\+ newdir/newfile.*\n'
807
br'\@\@ -1,1 \+0,0 \@\@\n'
695
810
self.assertContainsRe(self.differ.to_file.getvalue(),
696
"=== target is u'new'\n")
811
b"=== target is 'new'\n")
698
813
def test_diff_directory(self):
699
814
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(), '')
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'')
704
819
def create_old_new(self):
705
820
self.build_tree_contents([('old-tree/olddir/',),
706
('old-tree/olddir/oldfile', 'old\n')])
821
('old-tree/olddir/oldfile', b'old\n')])
707
822
self.old_tree.add('olddir')
708
self.old_tree.add('olddir/oldfile', 'file-id')
823
self.old_tree.add('olddir/oldfile', b'file-id')
709
824
self.build_tree_contents([('new-tree/newdir/',),
710
('new-tree/newdir/newfile', 'new\n')])
825
('new-tree/newdir/newfile', b'new\n')])
711
826
self.new_tree.add('newdir')
712
self.new_tree.add('newdir/newfile', 'file-id')
827
self.new_tree.add('newdir/newfile', b'file-id')
714
829
def test_register_diff(self):
715
830
self.create_old_new()
716
831
old_diff_factories = diff.DiffTree.diff_factories
717
diff.DiffTree.diff_factories=old_diff_factories[:]
832
diff.DiffTree.diff_factories = old_diff_factories[:]
718
833
diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
720
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
835
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
722
837
diff.DiffTree.diff_factories = old_diff_factories
723
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
838
differ.diff('olddir/oldfile', 'newdir/newfile')
724
839
self.assertNotContainsRe(
725
840
differ.to_file.getvalue(),
726
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
' \@\@\n-old\n\+new\n\n')
841
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
842
br' \@\@\n-old\n\+new\n\n')
728
843
self.assertContainsRe(differ.to_file.getvalue(),
729
'was: old\nis: new\n')
844
b'was: old\nis: new\n')
731
846
def test_extra_factories(self):
732
847
self.create_old_new()
733
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
848
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
734
849
extra_factories=[DiffWasIs.from_diff_tree])
735
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
850
differ.diff('olddir/oldfile', 'newdir/newfile')
736
851
self.assertNotContainsRe(
737
852
differ.to_file.getvalue(),
738
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
' \@\@\n-old\n\+new\n\n')
853
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
854
br' \@\@\n-old\n\+new\n\n')
740
855
self.assertContainsRe(differ.to_file.getvalue(),
741
'was: old\nis: new\n')
856
b'was: old\nis: new\n')
743
858
def test_alphabetical_order(self):
744
859
self.build_tree(['new-tree/a-file'])
747
862
self.old_tree.add('b-file')
748
863
self.differ.show_diff(None)
749
864
self.assertContainsRe(self.differ.to_file.getvalue(),
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)
865
b'.*a-file(.|\n)*b-file')
1283
868
class TestDiffFromTool(tests.TestCaseWithTransport):
1285
870
def test_from_string(self):
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)
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'))
1291
887
def test_from_string_u5(self):
1292
diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
888
diff_obj = diff.DiffFromTool.from_string(
889
['diff', "-u 5", '{old_path}', '{new_path}'], None, None, None)
1294
890
self.addCleanup(diff_obj.finish)
1295
self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
891
self.assertEqual(['diff', '-u 5', '{old_path}', '{new_path}'],
1296
892
diff_obj.command_template)
1297
893
self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1298
894
diff_obj._get_command('old-path', 'new-path'))
1300
896
def test_from_string_path_with_backslashes(self):
1301
self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
1302
tool = 'C:\\Tools\\Diff.exe'
897
self.requireFeature(features.backslashdir_feature)
898
tool = ['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}']
1303
899
diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1304
900
self.addCleanup(diff_obj.finish)
1305
self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
901
self.assertEqual(['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}'],
1306
902
diff_obj.command_template)
1307
903
self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1308
904
diff_obj._get_command('old-path', 'new-path'))
1310
906
def test_execute(self):
1312
diff_obj = diff.DiffFromTool(['python', '-c',
1313
'print "@old_path @new_path"'],
908
diff_obj = diff.DiffFromTool([sys.executable, '-c',
909
'print("{old_path} {new_path}")'],
1314
910
None, None, output)
1315
911
self.addCleanup(diff_obj.finish)
1316
912
diff_obj._execute('old', 'new')
1317
self.assertEqual(output.getvalue().rstrip(), 'old new')
913
self.assertEqual(output.getvalue().rstrip(), b'old new')
1319
def test_excute_missing(self):
915
def test_execute_missing(self):
1320
916
diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
917
None, None, None)
1322
918
self.addCleanup(diff_obj.finish)
1356
952
self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1358
954
def test_prepare_files(self):
1360
956
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')
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')
1365
961
# Earliest allowable date on FAT32 filesystems is 1980-01-01
1366
962
tree.commit('old tree', timestamp=315532800)
1367
963
tree.rename_one('oldname', 'newname')
1368
964
tree.rename_one('oldname2', 'newname2')
1369
self.build_tree_contents([('tree/newname', 'newcontent')])
1370
self.build_tree_contents([('tree/newname2', 'newcontent2')])
965
self.build_tree_contents([('tree/newname', b'newcontent')])
966
self.build_tree_contents([('tree/newname2', b'newcontent2')])
1371
967
old_tree = tree.basis_tree()
1372
968
old_tree.lock_read()
1373
969
self.addCleanup(old_tree.unlock)
1374
970
tree.lock_read()
1375
971
self.addCleanup(tree.unlock)
1376
diff_obj = diff.DiffFromTool(['python', '-c',
1377
'print "@old_path @new_path"'],
972
diff_obj = diff.DiffFromTool([sys.executable, '-c',
973
'print "{old_path} {new_path}"'],
1378
974
old_tree, tree, output)
1379
975
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',
976
self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
977
old_path, new_path = diff_obj._prepare_files(
978
'oldname', 'newname')
1383
979
self.assertContainsRe(old_path, 'old/oldname$')
1384
980
self.assertEqual(315532800, os.stat(old_path).st_mtime)
1385
981
self.assertContainsRe(new_path, 'tree/newname$')
1386
self.assertFileEqual('oldcontent', old_path)
1387
self.assertFileEqual('newcontent', new_path)
982
self.assertFileEqual(b'oldcontent', old_path)
983
self.assertFileEqual(b'newcontent', new_path)
1388
984
if osutils.host_os_dereferences_symlinks():
1389
985
self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
986
# make sure we can create files with the same parent directories
1391
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
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'))
1394
1030
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1396
1032
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.
1033
"""Call get_trees_and_branches_to_diff_locked."""
1034
exit_stack = contextlib.ExitStack()
1035
self.addCleanup(exit_stack.close)
1400
1036
return diff.get_trees_and_branches_to_diff_locked(
1401
path_list, revision_specs, old_url, new_url, self.addCleanup)
1037
path_list, revision_specs, old_url, new_url, exit_stack)
1403
1039
def test_basic(self):
1404
1040
tree = self.make_branch_and_tree('tree')