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)
83
115
class TestDiff(tests.TestCase):
85
117
def test_add_nl(self):
86
118
"""diff generates a valid diff for patches that add a newline"""
87
lines = udiff_lines(['boo'], ['boo\n'])
119
lines = udiff_lines([b'boo'], [b'boo\n'])
88
120
self.check_patch(lines)
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
90
## "expected no-nl, got %r" % lines[4]
121
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
122
## "expected no-nl, got %r" % lines[4]
92
124
def test_add_nl_2(self):
93
125
"""diff generates a valid diff for patches that change last line and
96
lines = udiff_lines(['boo'], ['goo\n'])
128
lines = udiff_lines([b'boo'], [b'goo\n'])
97
129
self.check_patch(lines)
98
self.assertEquals(lines[4], '\\ No newline at end of file\n')
99
## "expected no-nl, got %r" % lines[4]
130
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
131
## "expected no-nl, got %r" % lines[4]
101
133
def test_remove_nl(self):
102
134
"""diff generates a valid diff for patches that change last line and
105
lines = udiff_lines(['boo\n'], ['boo'])
137
lines = udiff_lines([b'boo\n'], [b'boo'])
106
138
self.check_patch(lines)
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
108
## "expected no-nl, got %r" % lines[5]
139
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
140
## "expected no-nl, got %r" % lines[5]
110
142
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)
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)
124
156
def test_binary_lines(self):
126
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
158
uni_lines = [1023 * b'a' + b'\x00']
159
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
160
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
udiff_lines(uni_lines , empty, allow_binary=True)
161
udiff_lines(uni_lines, empty, allow_binary=True)
130
162
udiff_lines(empty, uni_lines, allow_binary=True)
132
164
def test_external_diff(self):
133
lines = external_udiff_lines(['boo\n'], ['goo\n'])
165
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
134
166
self.check_patch(lines)
135
self.assertEqual('\n', lines[-1])
167
self.assertEqual(b'\n', lines[-1])
137
169
def test_external_diff_no_fileno(self):
138
170
# Make sure that we can handle not having a fileno, even
139
171
# if the diff is large
140
lines = external_udiff_lines(['boo\n']*10000,
172
lines = external_udiff_lines([b'boo\n'] * 10000,
142
174
use_stringio=True)
143
175
self.check_patch(lines)
145
177
def test_external_diff_binary_lang_c(self):
147
178
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)
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'])
160
187
def test_no_external_diff(self):
161
188
"""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
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'])
172
196
def test_internal_diff_default(self):
173
197
# Default internal diff encoding is utf8
175
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
u'new_\xe5', ['new_text\n'], output)
199
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
200
u'new_\xe5', [b'new_text\n'], output)
177
201
lines = output.getvalue().splitlines(True)
178
202
self.check_patch(lines)
179
self.assertEquals(['--- old_\xc2\xb5\n',
180
'+++ new_\xc3\xa5\n',
203
self.assertEqual([b'--- old_\xc2\xb5\n',
204
b'+++ new_\xc3\xa5\n',
205
b'@@ -1,1 +1,1 @@\n',
188
211
def test_internal_diff_utf8(self):
190
diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
u'new_\xe5', ['new_text\n'], output,
213
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
214
u'new_\xe5', [b'new_text\n'], output,
192
215
path_encoding='utf8')
193
216
lines = output.getvalue().splitlines(True)
194
217
self.check_patch(lines)
195
self.assertEquals(['--- old_\xc2\xb5\n',
196
'+++ new_\xc3\xa5\n',
218
self.assertEqual([b'--- old_\xc2\xb5\n',
219
b'+++ new_\xc3\xa5\n',
220
b'@@ -1,1 +1,1 @@\n',
204
226
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,
228
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
229
u'new_\xe5', [b'new_text\n'], output,
208
230
path_encoding='iso-8859-1')
209
231
lines = output.getvalue().splitlines(True)
210
232
self.check_patch(lines)
211
self.assertEquals(['--- old_\xb5\n',
233
self.assertEqual([b'--- old_\xb5\n',
235
b'@@ -1,1 +1,1 @@\n',
220
241
def test_internal_diff_no_content(self):
222
243
diff.internal_diff(u'old', [], u'new', [], output)
223
self.assertEqual('', output.getvalue())
244
self.assertEqual(b'', output.getvalue())
225
246
def test_internal_diff_no_changes(self):
227
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
u'new', ['text\n', 'contents\n'],
248
diff.internal_diff(u'old', [b'text\n', b'contents\n'],
249
u'new', [b'text\n', b'contents\n'],
230
self.assertEqual('', output.getvalue())
251
self.assertEqual(b'', output.getvalue())
232
253
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')
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',
241
317
class TestDiffFiles(tests.TestCaseInTempDir):
243
319
def test_external_diff_binary(self):
244
320
"""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')
245
323
# Make sure external_diff doesn't fail in the current LANG
246
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
324
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
248
326
cmd = ['diff', '-u', '--binary', 'old', 'new']
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('foo\x00bar\n')
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')
251
331
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
stdin=subprocess.PIPE)
332
stdin=subprocess.PIPE)
253
333
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
256
334
# 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):
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):
279
354
super(TestDiffDates, self).setUp()
280
355
self.wt = self.make_branch_and_tree('.')
281
356
self.b = self.wt.branch
282
357
self.build_tree_contents([
283
('file1', 'file1 contents at rev 1\n'),
284
('file2', 'file2 contents at rev 1\n')
358
('file1', b'file1 contents at rev 1\n'),
359
('file2', b'file2 contents at rev 1\n')
286
361
self.wt.add(['file1', 'file2'])
288
363
message='Revision 1',
289
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
364
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
292
self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
367
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
294
369
message='Revision 2',
295
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
370
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
298
self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
373
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
300
375
message='Revision 3',
301
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
376
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
304
379
self.wt.remove(['file2'])
306
381
message='Revision 4',
307
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
382
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
310
385
self.build_tree_contents([
311
('file1', 'file1 contents in working tree\n')
386
('file1', b'file1 contents in working tree\n')
313
388
# 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
389
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
316
391
def test_diff_rev_tree_working_tree(self):
317
output = self.get_diff(self.wt.basis_tree(), self.wt)
392
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
393
# note that the date for old/file1 is from rev 2 rather than from
319
394
# the basis revision (rev 4)
320
self.assertEqualDiff(output, '''\
395
self.assertEqualDiff(output, b'''\
321
396
=== modified file 'file1'
322
397
--- old/file1\t2006-04-02 00:00:00 +0000
323
398
+++ new/file1\t2006-04-05 00:00:00 +0000
393
468
self.wt.add(['dir1', 'dir2'])
394
469
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):
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):
407
481
"""Direct tests for show_diff_trees"""
409
483
def test_modified_file(self):
410
484
"""Test when a file is modified."""
411
485
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')
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')
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'
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')
424
498
def test_modified_file_in_renamed_dir(self):
425
499
"""Test when a file is modified in a renamed directory."""
426
500
tree = self.make_branch_and_tree('tree')
427
501
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')
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')
432
506
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")
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")
437
511
# XXX: This is technically incorrect, because it used to be at another
438
512
# 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'
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')
444
518
def test_renamed_directory(self):
445
519
"""Test when only a directory is only renamed."""
446
520
tree = self.make_branch_and_tree('tree')
447
521
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')
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')
452
526
tree.rename_one('dir', 'newdir')
453
d = self.get_diff(tree.basis_tree(), tree)
527
d = get_diff_as_string(tree.basis_tree(), tree)
454
528
# Renaming a directory should be a single "you renamed this dir" even
455
529
# when there are files inside.
456
self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
530
self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
458
532
def test_renamed_file(self):
459
533
"""Test when a file is only renamed."""
460
534
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')
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')
465
539
tree.rename_one('file', 'newname')
466
d = self.get_diff(tree.basis_tree(), tree)
467
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
540
d = get_diff_as_string(tree.basis_tree(), tree)
541
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
468
542
# We shouldn't have a --- or +++ line, because there is no content
470
self.assertNotContainsRe(d, '---')
544
self.assertNotContainsRe(d, b'---')
472
546
def test_renamed_and_modified_file(self):
473
547
"""Test when a file is only renamed."""
474
548
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')
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')
479
553
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'
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')
489
562
def test_internal_diff_exec_property(self):
490
563
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)
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)
500
tree.commit('one', rev_id='rev-1')
573
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'))
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'))
508
581
tree.rename_one('c', 'new-c')
509
582
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'")
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'")
525
597
def test_binary_unicode_filenames(self):
526
598
"""Test that contents of files are *not* encoded in UTF-8 when there
527
599
is a binary file in the diff.
529
601
# See https://bugs.launchpad.net/bugs/110092.
530
self.requireFeature(tests.UnicodeFilenameFeature)
602
self.requireFeature(features.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
534
604
tree = self.make_branch_and_tree('tree')
535
605
alpha, omega = u'\u03b1', u'\u03c9'
536
606
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
537
607
self.build_tree_contents(
538
[('tree/' + alpha, chr(0)),
608
[('tree/' + alpha, b'\0'),
539
609
('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()
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()
544
614
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"
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"
548
619
% (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,))
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,))
553
624
def test_unicode_filename(self):
554
625
"""Test when the filename are unicode."""
555
self.requireFeature(tests.UnicodeFilenameFeature)
626
self.requireFeature(features.UnicodeFilenameFeature)
557
628
alpha, omega = u'\u03b1', u'\u03c9'
558
629
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
560
631
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)
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)
577
648
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)
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)
584
698
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())
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())
594
707
class TestDiffTree(tests.TestCaseWithTransport):
601
714
self.new_tree = self.make_branch_and_tree('new-tree')
602
715
self.new_tree.lock_write()
603
716
self.addCleanup(self.new_tree.unlock)
604
self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
717
self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
606
719
def test_diff_text(self):
607
720
self.build_tree_contents([('old-tree/olddir/',),
608
('old-tree/olddir/oldfile', 'old\n')])
721
('old-tree/olddir/oldfile', b'old\n')])
609
722
self.old_tree.add('olddir')
610
self.old_tree.add('olddir/oldfile', 'file-id')
723
self.old_tree.add('olddir/oldfile', b'file-id')
611
724
self.build_tree_contents([('new-tree/newdir/',),
612
('new-tree/newdir/newfile', 'new\n')])
725
('new-tree/newdir/newfile', b'new\n')])
613
726
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',
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',
629
744
differ.to_file.getvalue())
631
746
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')
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')
636
751
os.unlink('new-tree/file')
637
752
self.differ.show_diff(None)
638
self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
753
self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
640
755
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')
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')
645
760
os.unlink('old-tree/file')
646
761
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
762
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
649
764
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
765
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
766
differ.diff_symlink('old target', None)
652
self.assertEqual("=== target was 'old target'\n",
767
self.assertEqual(b"=== target was 'old target'\n",
653
768
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
770
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
771
differ.diff_symlink(None, 'new target')
657
self.assertEqual("=== target is 'new target'\n",
772
self.assertEqual(b"=== target is 'new target'\n",
658
773
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
775
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
776
differ.diff_symlink('old target', 'new target')
662
self.assertEqual("=== target changed 'old target' => 'new target'\n",
777
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
663
778
differ.to_file.getvalue())
665
780
def test_diff(self):
666
781
self.build_tree_contents([('old-tree/olddir/',),
667
('old-tree/olddir/oldfile', 'old\n')])
782
('old-tree/olddir/oldfile', b'old\n')])
668
783
self.old_tree.add('olddir')
669
self.old_tree.add('olddir/oldfile', 'file-id')
784
self.old_tree.add('olddir/oldfile', b'file-id')
670
785
self.build_tree_contents([('new-tree/newdir/',),
671
('new-tree/newdir/newfile', 'new\n')])
786
('new-tree/newdir/newfile', b'new\n')])
672
787
self.new_tree.add('newdir')
673
self.new_tree.add('newdir/newfile', 'file-id')
674
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
788
self.new_tree.add('newdir/newfile', b'file-id')
789
self.differ.diff('olddir/oldfile', 'newdir/newfile')
675
790
self.assertContainsRe(
676
791
self.differ.to_file.getvalue(),
677
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
' \@\@\n-old\n\+new\n\n')
792
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
793
br' \@\@\n-old\n\+new\n\n')
680
795
def test_diff_kind_change(self):
681
self.requireFeature(tests.SymlinkFeature)
796
self.requireFeature(features.SymlinkFeature)
682
797
self.build_tree_contents([('old-tree/olddir/',),
683
('old-tree/olddir/oldfile', 'old\n')])
798
('old-tree/olddir/oldfile', b'old\n')])
684
799
self.old_tree.add('olddir')
685
self.old_tree.add('olddir/oldfile', 'file-id')
800
self.old_tree.add('olddir/oldfile', b'file-id')
686
801
self.build_tree(['new-tree/newdir/'])
687
802
os.symlink('new', 'new-tree/newdir/newfile')
688
803
self.new_tree.add('newdir')
689
self.new_tree.add('newdir/newfile', 'file-id')
690
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
804
self.new_tree.add('newdir/newfile', b'file-id')
805
self.differ.diff('olddir/oldfile', 'newdir/newfile')
691
806
self.assertContainsRe(
692
807
self.differ.to_file.getvalue(),
693
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
808
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
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 = cleanup.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')