79
"""Simple file-like object that allows writes with any type and records."""
82
self.write_record = []
84
def write(self, data):
85
self.write_record.append(data)
87
def check_types(self, testcase, expected_type):
89
any(not isinstance(o, expected_type) for o in self.write_record),
90
"Not all writes of type %s: %r" % (
91
expected_type.__name__, self.write_record))
94
class TestDiffOptions(tests.TestCase):
96
def test_unified_added(self):
97
"""Check for default style '-u' only if no other style specified
100
# Verify that style defaults to unified, id est '-u' appended
101
# to option list, in the absence of an alternative style.
102
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
105
class TestDiffOptionsScenarios(tests.TestCase):
107
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
108
style = None # Set by load_tests_apply_scenarios from scenarios
110
def test_unified_not_added(self):
111
# Verify that for all valid style options, '-u' is not
112
# appended to option list.
113
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
114
self.assertEqual(["%s" % (self.style,)], ret_opts)
83
117
class TestDiff(tests.TestCase):
85
119
def test_add_nl(self):
86
120
"""diff generates a valid diff for patches that add a newline"""
87
lines = udiff_lines(['boo'], ['boo\n'])
121
lines = udiff_lines([b'boo'], [b'boo\n'])
88
122
self.check_patch(lines)
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
123
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
90
124
## "expected no-nl, got %r" % lines[4]
92
126
def test_add_nl_2(self):
93
127
"""diff generates a valid diff for patches that change last line and
96
lines = udiff_lines(['boo'], ['goo\n'])
130
lines = udiff_lines([b'boo'], [b'goo\n'])
97
131
self.check_patch(lines)
98
self.assertEquals(lines[4], '\\ No newline at end of file\n')
132
self.assertEqual(lines[4], b'\\ No newline at end of file\n')
99
133
## "expected no-nl, got %r" % lines[4]
101
135
def test_remove_nl(self):
102
136
"""diff generates a valid diff for patches that change last line and
105
lines = udiff_lines(['boo\n'], ['boo'])
139
lines = udiff_lines([b'boo\n'], [b'boo'])
106
140
self.check_patch(lines)
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
141
self.assertEqual(lines[5], b'\\ No newline at end of file\n')
108
142
## "expected no-nl, got %r" % lines[5]
110
144
def check_patch(self, lines):
111
self.assert_(len(lines) > 1)
145
self.assertTrue(len(lines) > 1)
112
146
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
113
self.assert_(lines[0].startswith ('---'))
147
self.assertTrue(lines[0].startswith (b'---'))
114
148
## 'No orig line for patch:\n%s' % "".join(lines)
115
self.assert_(lines[1].startswith ('+++'))
149
self.assertTrue(lines[1].startswith (b'+++'))
116
150
## 'No mod line for patch:\n%s' % "".join(lines)
117
self.assert_(len(lines) > 2)
151
self.assertTrue(len(lines) > 2)
118
152
## "No hunks for patch:\n%s" % "".join(lines)
119
self.assert_(lines[2].startswith('@@'))
153
self.assertTrue(lines[2].startswith(b'@@'))
120
154
## "No hunk header for patch:\n%s" % "".join(lines)
121
self.assert_('@@' in lines[2][2:])
155
self.assertTrue(b'@@' in lines[2][2:])
122
156
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
158
def test_binary_lines(self):
126
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
160
uni_lines = [1023 * b'a' + b'\x00']
161
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
162
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
udiff_lines(uni_lines , empty, allow_binary=True)
163
udiff_lines(uni_lines, empty, allow_binary=True)
130
164
udiff_lines(empty, uni_lines, allow_binary=True)
132
166
def test_external_diff(self):
133
lines = external_udiff_lines(['boo\n'], ['goo\n'])
167
lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
134
168
self.check_patch(lines)
135
self.assertEqual('\n', lines[-1])
169
self.assertEqual(b'\n', lines[-1])
137
171
def test_external_diff_no_fileno(self):
138
172
# Make sure that we can handle not having a fileno, even
139
173
# if the diff is large
140
lines = external_udiff_lines(['boo\n']*10000,
174
lines = external_udiff_lines([b'boo\n']*10000,
142
176
use_stringio=True)
143
177
self.check_patch(lines)
145
179
def test_external_diff_binary_lang_c(self):
147
180
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
old_env[lang] = osutils.set_or_unset_env(lang, 'C')
150
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
151
# Older versions of diffutils say "Binary files", newer
152
# versions just say "Files".
153
self.assertContainsRe(lines[0],
154
'(Binary f|F)iles old and new differ\n')
155
self.assertEquals(lines[1:], ['\n'])
157
for lang, old_val in old_env.iteritems():
158
osutils.set_or_unset_env(lang, old_val)
181
self.overrideEnv(lang, 'C')
182
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
183
# Older versions of diffutils say "Binary files", newer
184
# versions just say "Files".
185
self.assertContainsRe(lines[0], b'(Binary f|F)iles old and new differ\n')
186
self.assertEqual(lines[1:], [b'\n'])
160
188
def test_no_external_diff(self):
161
189
"""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
190
# Make sure no 'diff' command is available
191
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
192
self.overrideEnv('PATH', '')
193
self.assertRaises(errors.NoDiff, diff.external_diff,
194
b'old', [b'boo\n'], b'new', [b'goo\n'],
195
BytesIO(), diff_opts=['-u'])
172
197
def test_internal_diff_default(self):
173
198
# Default internal diff encoding is utf8
175
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
u'new_\xe5', ['new_text\n'], output)
200
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
201
u'new_\xe5', [b'new_text\n'], output)
177
202
lines = output.getvalue().splitlines(True)
178
203
self.check_patch(lines)
179
self.assertEquals(['--- old_\xc2\xb5\n',
180
'+++ new_\xc3\xa5\n',
204
self.assertEqual([b'--- old_\xc2\xb5\n',
205
b'+++ new_\xc3\xa5\n',
206
b'@@ -1,1 +1,1 @@\n',
188
213
def test_internal_diff_utf8(self):
190
diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
u'new_\xe5', ['new_text\n'], output,
215
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
216
u'new_\xe5', [b'new_text\n'], output,
192
217
path_encoding='utf8')
193
218
lines = output.getvalue().splitlines(True)
194
219
self.check_patch(lines)
195
self.assertEquals(['--- old_\xc2\xb5\n',
196
'+++ new_\xc3\xa5\n',
220
self.assertEqual([b'--- old_\xc2\xb5\n',
221
b'+++ new_\xc3\xa5\n',
222
b'@@ -1,1 +1,1 @@\n',
204
229
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,
231
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
232
u'new_\xe5', [b'new_text\n'], output,
208
233
path_encoding='iso-8859-1')
209
234
lines = output.getvalue().splitlines(True)
210
235
self.check_patch(lines)
211
self.assertEquals(['--- old_\xb5\n',
236
self.assertEqual([b'--- old_\xb5\n',
238
b'@@ -1,1 +1,1 @@\n',
220
245
def test_internal_diff_no_content(self):
222
247
diff.internal_diff(u'old', [], u'new', [], output)
223
self.assertEqual('', output.getvalue())
248
self.assertEqual(b'', output.getvalue())
225
250
def test_internal_diff_no_changes(self):
227
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
u'new', ['text\n', 'contents\n'],
252
diff.internal_diff(u'old', [b'text\n', b'contents\n'],
253
u'new', [b'text\n', b'contents\n'],
230
self.assertEqual('', output.getvalue())
255
self.assertEqual(b'', output.getvalue())
232
257
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')
259
diff.internal_diff(u'old_\xb5', [b'old_text\n'],
260
u'new_\xe5', [b'new_text\n'], output)
261
output.check_types(self, bytes)
263
def test_internal_diff_default_context(self):
265
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
266
b'same_text\n', b'same_text\n', b'old_text\n'],
267
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
268
b'same_text\n', b'same_text\n', b'new_text\n'], output)
269
lines = output.getvalue().splitlines(True)
270
self.check_patch(lines)
271
self.assertEqual([b'--- old\n',
273
b'@@ -3,4 +3,4 @@\n',
283
def test_internal_diff_no_context(self):
285
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
286
b'same_text\n', b'same_text\n', b'old_text\n'],
287
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
288
b'same_text\n', b'same_text\n', b'new_text\n'], output,
290
lines = output.getvalue().splitlines(True)
291
self.check_patch(lines)
292
self.assertEqual([b'--- old\n',
294
b'@@ -6,1 +6,1 @@\n',
301
def test_internal_diff_more_context(self):
303
diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
304
b'same_text\n', b'same_text\n', b'old_text\n'],
305
'new', [b'same_text\n', b'same_text\n', b'same_text\n',
306
b'same_text\n', b'same_text\n', b'new_text\n'], output,
308
lines = output.getvalue().splitlines(True)
309
self.check_patch(lines)
310
self.assertEqual([b'--- old\n',
312
b'@@ -2,5 +2,5 @@\n',
241
325
class TestDiffFiles(tests.TestCaseInTempDir):
243
327
def test_external_diff_binary(self):
244
328
"""The output when using external diff should use diff's i18n error"""
329
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
330
self.overrideEnv(lang, 'C')
245
331
# Make sure external_diff doesn't fail in the current LANG
246
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
332
lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
248
334
cmd = ['diff', '-u', '--binary', 'old', 'new']
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('foo\x00bar\n')
335
with open('old', 'wb') as f: f.write(b'\x00foobar\n')
336
with open('new', 'wb') as f: f.write(b'foo\x00bar\n')
251
337
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
338
stdin=subprocess.PIPE)
253
339
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
256
340
# 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):
341
self.assertEqual(out.splitlines(True) + [b'\n'], lines)
344
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
346
if working_tree is not None:
347
extra_trees = (working_tree,)
350
diff.show_diff_trees(tree1, tree2, output,
351
specific_files=specific_files,
352
extra_trees=extra_trees, old_label='old/',
354
return output.getvalue()
357
class TestDiffDates(tests.TestCaseWithTransport):
279
360
super(TestDiffDates, self).setUp()
280
361
self.wt = self.make_branch_and_tree('.')
281
362
self.b = self.wt.branch
282
363
self.build_tree_contents([
283
('file1', 'file1 contents at rev 1\n'),
284
('file2', 'file2 contents at rev 1\n')
364
('file1', b'file1 contents at rev 1\n'),
365
('file2', b'file2 contents at rev 1\n')
286
367
self.wt.add(['file1', 'file2'])
288
369
message='Revision 1',
289
370
timestamp=1143849600, # 2006-04-01 00:00:00 UTC
292
self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
373
self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
294
375
message='Revision 2',
295
376
timestamp=1143936000, # 2006-04-02 00:00:00 UTC
298
self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
379
self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
300
381
message='Revision 3',
301
382
timestamp=1144022400, # 2006-04-03 00:00:00 UTC
304
385
self.wt.remove(['file2'])
306
387
message='Revision 4',
307
388
timestamp=1144108800, # 2006-04-04 00:00:00 UTC
310
391
self.build_tree_contents([
311
('file1', 'file1 contents in working tree\n')
392
('file1', b'file1 contents in working tree\n')
313
394
# set the date stamps for files in the working tree to known values
314
395
os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
316
397
def test_diff_rev_tree_working_tree(self):
317
output = self.get_diff(self.wt.basis_tree(), self.wt)
398
output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
399
# note that the date for old/file1 is from rev 2 rather than from
319
400
# the basis revision (rev 4)
320
self.assertEqualDiff(output, '''\
401
self.assertEqualDiff(output, b'''\
321
402
=== modified file 'file1'
322
403
--- old/file1\t2006-04-02 00:00:00 +0000
323
404
+++ new/file1\t2006-04-05 00:00:00 +0000
393
474
self.wt.add(['dir1', 'dir2'])
394
475
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):
476
old_tree = self.b.repository.revision_tree(b'rev-1')
477
new_tree = self.b.repository.revision_tree(b'rev-4')
478
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
479
working_tree=self.wt)
480
self.assertContainsRe(out, b'file1\t')
481
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
482
working_tree=self.wt)
483
self.assertNotContainsRe(out, b'file1\t')
486
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
487
"""Direct tests for show_diff_trees"""
409
489
def test_modified_file(self):
410
490
"""Test when a file is modified."""
411
491
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')
492
self.build_tree_contents([('tree/file', b'contents\n')])
493
tree.add(['file'], [b'file-id'])
494
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'
496
self.build_tree_contents([('tree/file', b'new contents\n')])
497
d = get_diff_as_string(tree.basis_tree(), tree)
498
self.assertContainsRe(d, b"=== modified file 'file'\n")
499
self.assertContainsRe(d, b'--- old/file\t')
500
self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
501
self.assertContainsRe(d, b'-contents\n'
502
b'\\+new contents\n')
424
504
def test_modified_file_in_renamed_dir(self):
425
505
"""Test when a file is modified in a renamed directory."""
426
506
tree = self.make_branch_and_tree('tree')
427
507
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')
508
self.build_tree_contents([('tree/dir/file', b'contents\n')])
509
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
510
tree.commit('one', rev_id=b'rev-1')
432
512
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")
513
self.build_tree_contents([('tree/other/file', b'new contents\n')])
514
d = get_diff_as_string(tree.basis_tree(), tree)
515
self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
516
self.assertContainsRe(d, b"=== modified file 'other/file'\n")
437
517
# XXX: This is technically incorrect, because it used to be at another
438
518
# 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'
519
self.assertContainsRe(d, b'--- old/dir/file\t')
520
self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
521
self.assertContainsRe(d, b'-contents\n'
522
b'\\+new contents\n')
444
524
def test_renamed_directory(self):
445
525
"""Test when only a directory is only renamed."""
446
526
tree = self.make_branch_and_tree('tree')
447
527
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')
528
self.build_tree_contents([('tree/dir/file', b'contents\n')])
529
tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
530
tree.commit('one', rev_id=b'rev-1')
452
532
tree.rename_one('dir', 'newdir')
453
d = self.get_diff(tree.basis_tree(), tree)
533
d = get_diff_as_string(tree.basis_tree(), tree)
454
534
# Renaming a directory should be a single "you renamed this dir" even
455
535
# when there are files inside.
456
self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
536
self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
458
538
def test_renamed_file(self):
459
539
"""Test when a file is only renamed."""
460
540
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')
541
self.build_tree_contents([('tree/file', b'contents\n')])
542
tree.add(['file'], [b'file-id'])
543
tree.commit('one', rev_id=b'rev-1')
465
545
tree.rename_one('file', 'newname')
466
d = self.get_diff(tree.basis_tree(), tree)
467
self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
546
d = get_diff_as_string(tree.basis_tree(), tree)
547
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
468
548
# We shouldn't have a --- or +++ line, because there is no content
470
self.assertNotContainsRe(d, '---')
550
self.assertNotContainsRe(d, b'---')
472
552
def test_renamed_and_modified_file(self):
473
553
"""Test when a file is only renamed."""
474
554
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')
555
self.build_tree_contents([('tree/file', b'contents\n')])
556
tree.add(['file'], [b'file-id'])
557
tree.commit('one', rev_id=b'rev-1')
479
559
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'
560
self.build_tree_contents([('tree/newname', b'new contents\n')])
561
d = get_diff_as_string(tree.basis_tree(), tree)
562
self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
563
self.assertContainsRe(d, b'--- old/file\t')
564
self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
565
self.assertContainsRe(d, b'-contents\n'
566
b'\\+new contents\n')
489
569
def test_internal_diff_exec_property(self):
490
570
tree = self.make_branch_and_tree('tree')
492
572
tt = transform.TreeTransform(tree)
493
tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
494
tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
495
tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
496
tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
497
tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
498
tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
573
tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
574
tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
575
tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
576
tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
577
tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
578
tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
500
tree.commit('one', rev_id='rev-1')
580
tree.commit('one', rev_id=b'rev-1')
502
582
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'))
583
tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
584
tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
585
tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
586
tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
508
588
tree.rename_one('c', 'new-c')
509
589
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'")
591
d = get_diff_as_string(tree.basis_tree(), tree)
593
self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
595
self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
597
self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
599
self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
601
self.assertNotContainsRe(d, br"file 'e'")
602
self.assertNotContainsRe(d, br"file 'f'")
525
604
def test_binary_unicode_filenames(self):
526
605
"""Test that contents of files are *not* encoded in UTF-8 when there
527
606
is a binary file in the diff.
529
608
# See https://bugs.launchpad.net/bugs/110092.
530
self.requireFeature(tests.UnicodeFilenameFeature)
609
self.requireFeature(features.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
534
611
tree = self.make_branch_and_tree('tree')
535
612
alpha, omega = u'\u03b1', u'\u03c9'
536
613
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
537
614
self.build_tree_contents(
538
[('tree/' + alpha, chr(0)),
615
[('tree/' + alpha, b'\0'),
539
616
('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()
617
(b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
618
tree.add([alpha], [b'file-id'])
619
tree.add([omega], [b'file-id-2'])
620
diff_content = StubO()
544
621
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"
622
diff_content.check_types(self, bytes)
623
d = b''.join(diff_content.write_record)
624
self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
625
self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
548
626
% (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,))
627
self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
628
self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
629
self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
553
631
def test_unicode_filename(self):
554
632
"""Test when the filename are unicode."""
555
self.requireFeature(tests.UnicodeFilenameFeature)
633
self.requireFeature(features.UnicodeFilenameFeature)
557
635
alpha, omega = u'\u03b1', u'\u03c9'
558
636
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
560
638
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'])
639
self.build_tree_contents([('tree/ren_'+alpha, b'contents\n')])
640
tree.add(['ren_'+alpha], [b'file-id-2'])
641
self.build_tree_contents([('tree/del_'+alpha, b'contents\n')])
642
tree.add(['del_'+alpha], [b'file-id-3'])
643
self.build_tree_contents([('tree/mod_'+alpha, b'contents\n')])
644
tree.add(['mod_'+alpha], [b'file-id-4'])
568
tree.commit('one', rev_id='rev-1')
646
tree.commit('one', rev_id=b'rev-1')
570
648
tree.rename_one('ren_'+alpha, 'ren_'+omega)
571
649
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')])
650
self.build_tree_contents([('tree/add_'+alpha, b'contents\n')])
651
tree.add(['add_'+alpha], [b'file-id'])
652
self.build_tree_contents([('tree/mod_'+alpha, b'contents_mod\n')])
576
d = self.get_diff(tree.basis_tree(), tree)
654
d = get_diff_as_string(tree.basis_tree(), tree)
577
655
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)
656
b"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
657
self.assertContainsRe(d, b"=== added file 'add_%s'"%autf8)
658
self.assertContainsRe(d, b"=== modified file 'mod_%s'"%autf8)
659
self.assertContainsRe(d, b"=== removed file 'del_%s'"%autf8)
661
def test_unicode_filename_path_encoding(self):
662
"""Test for bug #382699: unicode filenames on Windows should be shown
665
self.requireFeature(features.UnicodeFilenameFeature)
666
# The word 'test' in Russian
667
_russian_test = u'\u0422\u0435\u0441\u0442'
668
directory = _russian_test + u'/'
669
test_txt = _russian_test + u'.txt'
670
u1234 = u'\u1234.txt'
672
tree = self.make_branch_and_tree('.')
673
self.build_tree_contents([
674
(test_txt, b'foo\n'),
678
tree.add([test_txt, u1234, directory])
681
diff.show_diff_trees(tree.basis_tree(), tree, sio,
682
path_encoding='cp1251')
684
output = subst_dates(sio.getvalue())
686
=== added directory '%(directory)s'
687
=== added file '%(test_txt)s'
688
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
689
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
693
=== added file '?.txt'
694
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
695
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
699
''' % {b'directory': _russian_test.encode('cp1251'),
700
b'test_txt': test_txt.encode('cp1251'),
702
self.assertEqualDiff(output, shouldbe)
584
705
class DiffWasIs(diff.DiffPath):
586
707
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())
708
self.to_file.write(b'was: ')
709
self.to_file.write(self.old_tree.get_file(old_path).read())
710
self.to_file.write(b'is: ')
711
self.to_file.write(self.new_tree.get_file(new_path).read())
594
714
class TestDiffTree(tests.TestCaseWithTransport):
601
721
self.new_tree = self.make_branch_and_tree('new-tree')
602
722
self.new_tree.lock_write()
603
723
self.addCleanup(self.new_tree.unlock)
604
self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
724
self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
606
726
def test_diff_text(self):
607
727
self.build_tree_contents([('old-tree/olddir/',),
608
('old-tree/olddir/oldfile', 'old\n')])
728
('old-tree/olddir/oldfile', b'old\n')])
609
729
self.old_tree.add('olddir')
610
self.old_tree.add('olddir/oldfile', 'file-id')
730
self.old_tree.add('olddir/oldfile', b'file-id')
611
731
self.build_tree_contents([('new-tree/newdir/',),
612
('new-tree/newdir/newfile', 'new\n')])
732
('new-tree/newdir/newfile', b'new\n')])
613
733
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',
734
self.new_tree.add('newdir/newfile', b'file-id')
735
differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
736
differ.diff_text('olddir/oldfile', None, 'old label',
737
'new label', b'file-id', None)
739
b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
740
differ.to_file.getvalue())
741
differ.to_file.seek(0)
742
differ.diff_text(None, 'newdir/newfile',
743
'old label', 'new label', None, b'file-id')
745
b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
746
differ.to_file.getvalue())
747
differ.to_file.seek(0)
748
differ.diff_text('olddir/oldfile', 'newdir/newfile',
749
'old label', 'new label', b'file-id', b'file-id')
751
b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
629
752
differ.to_file.getvalue())
631
754
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')
755
self.build_tree_contents([('old-tree/file', b'contents'),
756
('new-tree/file', b'contents')])
757
self.old_tree.add('file', b'file-id')
758
self.new_tree.add('file', b'file-id')
636
759
os.unlink('new-tree/file')
637
760
self.differ.show_diff(None)
638
self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
761
self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
640
763
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')
764
self.build_tree_contents([('old-tree/file', b'contents'),
765
('new-tree/file', b'contents')])
766
self.old_tree.add('file', b'file-id')
767
self.new_tree.add('file', b'file-id')
645
768
os.unlink('old-tree/file')
646
769
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
770
self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
649
772
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
773
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
774
differ.diff_symlink('old target', None)
652
self.assertEqual("=== target was 'old target'\n",
775
self.assertEqual(b"=== target was 'old target'\n",
653
776
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
778
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
779
differ.diff_symlink(None, 'new target')
657
self.assertEqual("=== target is 'new target'\n",
780
self.assertEqual(b"=== target is 'new target'\n",
658
781
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
783
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
784
differ.diff_symlink('old target', 'new target')
662
self.assertEqual("=== target changed 'old target' => 'new target'\n",
785
self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
663
786
differ.to_file.getvalue())
665
788
def test_diff(self):
666
789
self.build_tree_contents([('old-tree/olddir/',),
667
('old-tree/olddir/oldfile', 'old\n')])
790
('old-tree/olddir/oldfile', b'old\n')])
668
791
self.old_tree.add('olddir')
669
self.old_tree.add('olddir/oldfile', 'file-id')
792
self.old_tree.add('olddir/oldfile', b'file-id')
670
793
self.build_tree_contents([('new-tree/newdir/',),
671
('new-tree/newdir/newfile', 'new\n')])
794
('new-tree/newdir/newfile', b'new\n')])
672
795
self.new_tree.add('newdir')
673
self.new_tree.add('newdir/newfile', 'file-id')
674
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
796
self.new_tree.add('newdir/newfile', b'file-id')
797
self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
675
798
self.assertContainsRe(
676
799
self.differ.to_file.getvalue(),
677
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
' \@\@\n-old\n\+new\n\n')
800
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
801
br' \@\@\n-old\n\+new\n\n')
680
803
def test_diff_kind_change(self):
681
self.requireFeature(tests.SymlinkFeature)
804
self.requireFeature(features.SymlinkFeature)
682
805
self.build_tree_contents([('old-tree/olddir/',),
683
('old-tree/olddir/oldfile', 'old\n')])
806
('old-tree/olddir/oldfile', b'old\n')])
684
807
self.old_tree.add('olddir')
685
self.old_tree.add('olddir/oldfile', 'file-id')
808
self.old_tree.add('olddir/oldfile', b'file-id')
686
809
self.build_tree(['new-tree/newdir/'])
687
810
os.symlink('new', 'new-tree/newdir/newfile')
688
811
self.new_tree.add('newdir')
689
self.new_tree.add('newdir/newfile', 'file-id')
690
self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
812
self.new_tree.add('newdir/newfile', b'file-id')
813
self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
691
814
self.assertContainsRe(
692
815
self.differ.to_file.getvalue(),
693
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
816
br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
695
818
self.assertContainsRe(self.differ.to_file.getvalue(),
696
"=== target is u'new'\n")
819
b"=== target is 'new'\n")
698
821
def test_diff_directory(self):
699
822
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(), '')
823
self.new_tree.add('new-dir', b'new-dir-id')
824
self.differ.diff(b'new-dir-id', None, 'new-dir')
825
self.assertEqual(self.differ.to_file.getvalue(), b'')
704
827
def create_old_new(self):
705
828
self.build_tree_contents([('old-tree/olddir/',),
706
('old-tree/olddir/oldfile', 'old\n')])
829
('old-tree/olddir/oldfile', b'old\n')])
707
830
self.old_tree.add('olddir')
708
self.old_tree.add('olddir/oldfile', 'file-id')
831
self.old_tree.add('olddir/oldfile', b'file-id')
709
832
self.build_tree_contents([('new-tree/newdir/',),
710
('new-tree/newdir/newfile', 'new\n')])
833
('new-tree/newdir/newfile', b'new\n')])
711
834
self.new_tree.add('newdir')
712
self.new_tree.add('newdir/newfile', 'file-id')
835
self.new_tree.add('newdir/newfile', b'file-id')
714
837
def test_register_diff(self):
715
838
self.create_old_new()
764
887
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
888
sm = self._PatienceSequenceMatcher(None, a, b)
766
889
mb = sm.get_matching_blocks()
767
self.assertEquals(35, len(mb))
890
self.assertEqual(35, len(mb))
769
892
def test_unique_lcs(self):
770
893
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)])
894
self.assertEqual(unique_lcs('', ''), [])
895
self.assertEqual(unique_lcs('', 'a'), [])
896
self.assertEqual(unique_lcs('a', ''), [])
897
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
898
self.assertEqual(unique_lcs('a', 'b'), [])
899
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
900
self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
901
self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
902
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
904
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
783
906
def test_recurse_matches(self):
784
907
def test_one(a, b, matches):
785
908
test_matches = []
786
909
self._recurse_matches(
787
910
a, b, 0, 0, len(a), len(b), test_matches, 10)
788
self.assertEquals(test_matches, matches)
911
self.assertEqual(test_matches, matches)
790
913
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
914
[(0, 0), (2, 2), (4, 4)])
896
1019
def test_opcodes(self):
897
1020
def chk_ops(a, b, expected_codes):
898
1021
s = self._PatienceSequenceMatcher(None, a, b)
899
self.assertEquals(expected_codes, s.get_opcodes())
1022
self.assertEqual(expected_codes, s.get_opcodes())
901
1024
chk_ops('', '', [])
902
1025
chk_ops([], [], [])
903
chk_ops('abc', '', [('delete', 0,3, 0,0)])
904
chk_ops('', 'abc', [('insert', 0,0, 0,3)])
905
chk_ops('abcd', 'abcd', [('equal', 0,4, 0,4)])
906
chk_ops('abcd', 'abce', [('equal', 0,3, 0,3),
907
('replace', 3,4, 3,4)
909
chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
913
chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
1026
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1027
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1028
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1029
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1030
('replace', 3, 4, 3, 4)
1032
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1033
('equal', 1, 4, 0, 3),
1034
('insert', 4, 4, 3, 4)
1036
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1037
('equal', 1, 5, 0, 4)
916
chk_ops('abcde', 'abXde', [('equal', 0,2, 0,2),
917
('replace', 2,3, 2,3),
1039
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1040
('replace', 2, 3, 2, 3),
1041
('equal', 3, 5, 3, 5)
920
chk_ops('abcde', 'abXYZde', [('equal', 0,2, 0,2),
921
('replace', 2,3, 2,5),
1043
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1044
('replace', 2, 3, 2, 5),
1045
('equal', 3, 5, 5, 7)
924
chk_ops('abde', 'abXYZde', [('equal', 0,2, 0,2),
925
('insert', 2,2, 2,5),
1047
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1048
('insert', 2, 2, 2, 5),
1049
('equal', 2, 4, 5, 7)
928
1051
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
[('equal', 0,6, 0,6),
930
('insert', 6,6, 6,11),
931
('equal', 6,16, 11,21)
1052
[('equal', 0, 6, 0, 6),
1053
('insert', 6, 6, 6, 11),
1054
('equal', 6, 16, 11, 21)
934
1057
[ 'hello there\n'
1356
1478
self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1358
1480
def test_prepare_files(self):
1360
1482
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')
1483
self.build_tree_contents([('tree/oldname', b'oldcontent')])
1484
self.build_tree_contents([('tree/oldname2', b'oldcontent2')])
1485
tree.add('oldname', b'file-id')
1486
tree.add('oldname2', b'file2-id')
1365
1487
# Earliest allowable date on FAT32 filesystems is 1980-01-01
1366
1488
tree.commit('old tree', timestamp=315532800)
1367
1489
tree.rename_one('oldname', 'newname')
1368
1490
tree.rename_one('oldname2', 'newname2')
1369
self.build_tree_contents([('tree/newname', 'newcontent')])
1370
self.build_tree_contents([('tree/newname2', 'newcontent2')])
1491
self.build_tree_contents([('tree/newname', b'newcontent')])
1492
self.build_tree_contents([('tree/newname2', b'newcontent2')])
1371
1493
old_tree = tree.basis_tree()
1372
1494
old_tree.lock_read()
1373
1495
self.addCleanup(old_tree.unlock)
1374
1496
tree.lock_read()
1375
1497
self.addCleanup(tree.unlock)
1376
diff_obj = diff.DiffFromTool(['python', '-c',
1498
diff_obj = diff.DiffFromTool([sys.executable, '-c',
1377
1499
'print "@old_path @new_path"'],
1378
1500
old_tree, tree, output)
1379
1501
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',
1502
self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
1503
old_path, new_path = diff_obj._prepare_files(
1504
'oldname', 'newname', file_id=b'file-id')
1383
1505
self.assertContainsRe(old_path, 'old/oldname$')
1384
1506
self.assertEqual(315532800, os.stat(old_path).st_mtime)
1385
1507
self.assertContainsRe(new_path, 'tree/newname$')
1386
self.assertFileEqual('oldcontent', old_path)
1387
self.assertFileEqual('newcontent', new_path)
1508
self.assertFileEqual(b'oldcontent', old_path)
1509
self.assertFileEqual(b'newcontent', new_path)
1388
1510
if osutils.host_os_dereferences_symlinks():
1389
1511
self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
1512
# make sure we can create files with the same parent directories
1391
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1513
diff_obj._prepare_files('oldname2', 'newname2', file_id=b'file2-id')
1516
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1518
def test_encodable_filename(self):
1519
# Just checks file path for external diff tool.
1520
# We cannot change CPython's internal encoding used by os.exec*.
1521
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1523
for _, scenario in EncodingAdapter.encoding_scenarios:
1524
encoding = scenario['encoding']
1525
dirname = scenario['info']['directory']
1526
filename = scenario['info']['filename']
1528
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1529
relpath = dirname + u'/' + filename
1530
fullpath = diffobj._safe_filename('safe', relpath)
1531
self.assertEqual(fullpath,
1532
fullpath.encode(encoding).decode(encoding))
1533
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1535
def test_unencodable_filename(self):
1536
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1538
for _, scenario in EncodingAdapter.encoding_scenarios:
1539
encoding = scenario['encoding']
1540
dirname = scenario['info']['directory']
1541
filename = scenario['info']['filename']
1543
if encoding == 'iso-8859-1':
1544
encoding = 'iso-8859-2'
1546
encoding = 'iso-8859-1'
1548
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1549
relpath = dirname + u'/' + filename
1550
fullpath = diffobj._safe_filename('safe', relpath)
1551
self.assertEqual(fullpath,
1552
fullpath.encode(encoding).decode(encoding))
1553
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1394
1556
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1396
1558
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.
1559
"""Call get_trees_and_branches_to_diff_locked."""
1400
1560
return diff.get_trees_and_branches_to_diff_locked(
1401
1561
path_list, revision_specs, old_url, new_url, self.addCleanup)