15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
18
from cStringIO import StringIO
23
from tempfile import TemporaryFile
25
from bzrlib import tests
26
from bzrlib.diff import (
35
get_trees_and_branches_to_diff,
28
revision as _mod_revision,
37
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
38
import bzrlib.osutils as osutils
39
import bzrlib.revision as _mod_revision
40
import bzrlib.transform as transform
41
import bzrlib.patiencediff
42
import bzrlib._patiencediff_py
43
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
44
TestCaseInTempDir, TestSkipped)
45
from bzrlib.revisiontree import RevisionTree
46
from bzrlib.revisionspec import RevisionSpec
49
class _AttribFeature(Feature):
52
if (sys.platform not in ('cygwin', 'win32')):
55
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
58
return (0 == proc.wait())
60
def feature_name(self):
61
return 'attrib Windows command-line tool'
63
AttribFeature = _AttribFeature()
66
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
67
'bzrlib._patiencediff_c')
34
from bzrlib.tests import (
38
from bzrlib.tests.blackbox.test_diff import subst_dates
39
from bzrlib.tests.scenarios import load_tests_apply_scenarios
42
load_tests = load_tests_apply_scenarios
70
45
def udiff_lines(old, new, allow_binary=False):
71
46
output = StringIO()
72
internal_diff('old', old, 'new', new, output, allow_binary)
47
diff.internal_diff('old', old, 'new', new, output, allow_binary)
74
49
return output.readlines()
79
54
# StringIO has no fileno, so it tests a different codepath
80
55
output = StringIO()
82
output = TemporaryFile()
57
output = tempfile.TemporaryFile()
84
external_diff('old', old, 'new', new, output, diff_opts=['-u'])
86
raise TestSkipped('external "diff" not present to test')
59
diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
61
raise tests.TestSkipped('external "diff" not present to test')
88
63
lines = output.readlines()
93
class TestDiff(TestCase):
68
class TestDiffOptions(tests.TestCase):
70
def test_unified_added(self):
71
"""Check for default style '-u' only if no other style specified
74
# Verify that style defaults to unified, id est '-u' appended
75
# to option list, in the absence of an alternative style.
76
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
79
class TestDiffOptionsScenarios(tests.TestCase):
81
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
82
style = None # Set by load_tests_apply_scenarios from scenarios
84
def test_unified_not_added(self):
85
# Verify that for all valid style options, '-u' is not
86
# appended to option list.
87
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
88
self.assertEqual(["%s" % (self.style,)], ret_opts)
91
class TestDiff(tests.TestCase):
95
93
def test_add_nl(self):
96
94
"""diff generates a valid diff for patches that add a newline"""
132
130
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
134
132
def test_binary_lines(self):
135
self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
136
self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
137
udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
138
udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
134
uni_lines = [1023 * 'a' + '\x00']
135
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
136
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
137
udiff_lines(uni_lines , empty, allow_binary=True)
138
udiff_lines(empty, uni_lines, allow_binary=True)
140
140
def test_external_diff(self):
141
141
lines = external_udiff_lines(['boo\n'], ['goo\n'])
151
151
self.check_patch(lines)
153
153
def test_external_diff_binary_lang_c(self):
155
154
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
156
old_env[lang] = osutils.set_or_unset_env(lang, 'C')
158
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
159
# Older versions of diffutils say "Binary files", newer
160
# versions just say "Files".
161
self.assertContainsRe(lines[0],
162
'(Binary f|F)iles old and new differ\n')
163
self.assertEquals(lines[1:], ['\n'])
165
for lang, old_val in old_env.iteritems():
166
osutils.set_or_unset_env(lang, old_val)
155
self.overrideEnv(lang, 'C')
156
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
157
# Older versions of diffutils say "Binary files", newer
158
# versions just say "Files".
159
self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
160
self.assertEquals(lines[1:], ['\n'])
168
162
def test_no_external_diff(self):
169
163
"""Check that NoDiff is raised when diff is not available"""
170
# Use os.environ['PATH'] to make sure no 'diff' command is available
171
orig_path = os.environ['PATH']
173
os.environ['PATH'] = ''
174
self.assertRaises(NoDiff, external_diff,
175
'old', ['boo\n'], 'new', ['goo\n'],
176
StringIO(), diff_opts=['-u'])
178
os.environ['PATH'] = orig_path
164
# Make sure no 'diff' command is available
165
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
166
self.overrideEnv('PATH', '')
167
self.assertRaises(errors.NoDiff, diff.external_diff,
168
'old', ['boo\n'], 'new', ['goo\n'],
169
StringIO(), diff_opts=['-u'])
180
171
def test_internal_diff_default(self):
181
172
# Default internal diff encoding is utf8
182
173
output = StringIO()
183
internal_diff(u'old_\xb5', ['old_text\n'],
184
u'new_\xe5', ['new_text\n'], output)
174
diff.internal_diff(u'old_\xb5', ['old_text\n'],
175
u'new_\xe5', ['new_text\n'], output)
185
176
lines = output.getvalue().splitlines(True)
186
177
self.check_patch(lines)
187
178
self.assertEquals(['--- old_\xc2\xb5\n',
228
219
def test_internal_diff_no_content(self):
229
220
output = StringIO()
230
internal_diff(u'old', [], u'new', [], output)
221
diff.internal_diff(u'old', [], u'new', [], output)
231
222
self.assertEqual('', output.getvalue())
233
224
def test_internal_diff_no_changes(self):
234
225
output = StringIO()
235
internal_diff(u'old', ['text\n', 'contents\n'],
236
u'new', ['text\n', 'contents\n'],
226
diff.internal_diff(u'old', ['text\n', 'contents\n'],
227
u'new', ['text\n', 'contents\n'],
238
229
self.assertEqual('', output.getvalue())
240
231
def test_internal_diff_returns_bytes(self):
242
233
output = StringIO.StringIO()
243
internal_diff(u'old_\xb5', ['old_text\n'],
244
u'new_\xe5', ['new_text\n'], output)
245
self.failUnless(isinstance(output.getvalue(), str),
234
diff.internal_diff(u'old_\xb5', ['old_text\n'],
235
u'new_\xe5', ['new_text\n'], output)
236
self.assertIsInstance(output.getvalue(), str,
246
237
'internal_diff should return bytestrings')
249
class TestDiffFiles(TestCaseInTempDir):
239
def test_internal_diff_default_context(self):
241
diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
242
'same_text\n','same_text\n','old_text\n'],
243
'new', ['same_text\n','same_text\n','same_text\n',
244
'same_text\n','same_text\n','new_text\n'], output)
245
lines = output.getvalue().splitlines(True)
246
self.check_patch(lines)
247
self.assertEquals(['--- old\n',
259
def test_internal_diff_no_context(self):
261
diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
262
'same_text\n','same_text\n','old_text\n'],
263
'new', ['same_text\n','same_text\n','same_text\n',
264
'same_text\n','same_text\n','new_text\n'], output,
266
lines = output.getvalue().splitlines(True)
267
self.check_patch(lines)
268
self.assertEquals(['--- old\n',
277
def test_internal_diff_more_context(self):
279
diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
280
'same_text\n','same_text\n','old_text\n'],
281
'new', ['same_text\n','same_text\n','same_text\n',
282
'same_text\n','same_text\n','new_text\n'], output,
284
lines = output.getvalue().splitlines(True)
285
self.check_patch(lines)
286
self.assertEquals(['--- old\n',
303
class TestDiffFiles(tests.TestCaseInTempDir):
251
305
def test_external_diff_binary(self):
252
306
"""The output when using external diff should use diff's i18n error"""
265
319
self.assertEqual(out.splitlines(True) + ['\n'], lines)
268
class TestShowDiffTreesHelper(TestCaseWithTransport):
269
"""Has a helper for running show_diff_trees"""
271
def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
273
if working_tree is not None:
274
extra_trees = (working_tree,)
277
show_diff_trees(tree1, tree2, output, specific_files=specific_files,
278
extra_trees=extra_trees, old_label='old/',
280
return output.getvalue()
283
class TestDiffDates(TestShowDiffTreesHelper):
322
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
324
if working_tree is not None:
325
extra_trees = (working_tree,)
328
diff.show_diff_trees(tree1, tree2, output,
329
specific_files=specific_files,
330
extra_trees=extra_trees, old_label='old/',
332
return output.getvalue()
335
class TestDiffDates(tests.TestCaseWithTransport):
286
338
super(TestDiffDates, self).setUp()
401
453
self.wt.rename_one('file1', 'dir1/file1')
402
454
old_tree = self.b.repository.revision_tree('rev-1')
403
455
new_tree = self.b.repository.revision_tree('rev-4')
404
out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
456
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
405
457
working_tree=self.wt)
406
458
self.assertContainsRe(out, 'file1\t')
407
out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
459
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
408
460
working_tree=self.wt)
409
461
self.assertNotContainsRe(out, 'file1\t')
413
class TestShowDiffTrees(TestShowDiffTreesHelper):
464
class TestShowDiffTrees(tests.TestCaseWithTransport):
414
465
"""Direct tests for show_diff_trees"""
416
467
def test_modified_file(self):
421
472
tree.commit('one', rev_id='rev-1')
423
474
self.build_tree_contents([('tree/file', 'new contents\n')])
424
diff = self.get_diff(tree.basis_tree(), tree)
425
self.assertContainsRe(diff, "=== modified file 'file'\n")
426
self.assertContainsRe(diff, '--- old/file\t')
427
self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
428
self.assertContainsRe(diff, '-contents\n'
475
d = get_diff_as_string(tree.basis_tree(), tree)
476
self.assertContainsRe(d, "=== modified file 'file'\n")
477
self.assertContainsRe(d, '--- old/file\t')
478
self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
479
self.assertContainsRe(d, '-contents\n'
431
482
def test_modified_file_in_renamed_dir(self):
432
483
"""Test when a file is modified in a renamed directory."""
439
490
tree.rename_one('dir', 'other')
440
491
self.build_tree_contents([('tree/other/file', 'new contents\n')])
441
diff = self.get_diff(tree.basis_tree(), tree)
442
self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
443
self.assertContainsRe(diff, "=== modified file 'other/file'\n")
492
d = get_diff_as_string(tree.basis_tree(), tree)
493
self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
494
self.assertContainsRe(d, "=== modified file 'other/file'\n")
444
495
# XXX: This is technically incorrect, because it used to be at another
445
496
# location. What to do?
446
self.assertContainsRe(diff, '--- old/dir/file\t')
447
self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
448
self.assertContainsRe(diff, '-contents\n'
497
self.assertContainsRe(d, '--- old/dir/file\t')
498
self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
499
self.assertContainsRe(d, '-contents\n'
451
502
def test_renamed_directory(self):
452
503
"""Test when only a directory is only renamed."""
515
566
tree.rename_one('c', 'new-c')
516
567
tree.rename_one('d', 'new-d')
518
diff = self.get_diff(tree.basis_tree(), tree)
520
self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
521
self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
522
self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
523
self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
524
self.assertNotContainsRe(diff, r"file 'e'")
525
self.assertNotContainsRe(diff, r"file 'f'")
569
d = get_diff_as_string(tree.basis_tree(), tree)
571
self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
573
self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
575
self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
577
self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
579
self.assertNotContainsRe(d, r"file 'e'")
580
self.assertNotContainsRe(d, r"file 'f'")
528
582
def test_binary_unicode_filenames(self):
529
583
"""Test that contents of files are *not* encoded in UTF-8 when there
530
584
is a binary file in the diff.
532
586
# See https://bugs.launchpad.net/bugs/110092.
533
self.requireFeature(tests.UnicodeFilenameFeature)
587
self.requireFeature(features.UnicodeFilenameFeature)
535
589
# This bug isn't triggered with cStringIO.
536
590
from StringIO import StringIO
544
598
tree.add([alpha], ['file-id'])
545
599
tree.add([omega], ['file-id-2'])
546
600
diff_content = StringIO()
547
show_diff_trees(tree.basis_tree(), tree, diff_content)
548
diff = diff_content.getvalue()
549
self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
550
self.assertContainsRe(
551
diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
552
self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
553
self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
554
self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
601
diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
602
d = diff_content.getvalue()
603
self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
604
self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
605
% (alpha_utf8, alpha_utf8))
606
self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
607
self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
608
self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
556
610
def test_unicode_filename(self):
557
611
"""Test when the filename are unicode."""
558
self.requireFeature(tests.UnicodeFilenameFeature)
612
self.requireFeature(features.UnicodeFilenameFeature)
560
614
alpha, omega = u'\u03b1', u'\u03c9'
561
615
autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
576
630
tree.add(['add_'+alpha], ['file-id'])
577
631
self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
579
diff = self.get_diff(tree.basis_tree(), tree)
580
self.assertContainsRe(diff,
633
d = get_diff_as_string(tree.basis_tree(), tree)
634
self.assertContainsRe(d,
581
635
"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
582
self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
583
self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
584
self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
587
class DiffWasIs(DiffPath):
636
self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
637
self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
638
self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
640
def test_unicode_filename_path_encoding(self):
641
"""Test for bug #382699: unicode filenames on Windows should be shown
644
self.requireFeature(features.UnicodeFilenameFeature)
645
# The word 'test' in Russian
646
_russian_test = u'\u0422\u0435\u0441\u0442'
647
directory = _russian_test + u'/'
648
test_txt = _russian_test + u'.txt'
649
u1234 = u'\u1234.txt'
651
tree = self.make_branch_and_tree('.')
652
self.build_tree_contents([
657
tree.add([test_txt, u1234, directory])
660
diff.show_diff_trees(tree.basis_tree(), tree, sio,
661
path_encoding='cp1251')
663
output = subst_dates(sio.getvalue())
665
=== added directory '%(directory)s'
666
=== added file '%(test_txt)s'
667
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
668
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
672
=== added file '?.txt'
673
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
674
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
678
''' % {'directory': _russian_test.encode('cp1251'),
679
'test_txt': test_txt.encode('cp1251'),
681
self.assertEqualDiff(output, shouldbe)
684
class DiffWasIs(diff.DiffPath):
589
686
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
590
687
self.to_file.write('was: ')
650
747
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
652
749
def test_diff_symlink(self):
653
differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
750
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
654
751
differ.diff_symlink('old target', None)
655
752
self.assertEqual("=== target was 'old target'\n",
656
753
differ.to_file.getvalue())
658
differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
755
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
659
756
differ.diff_symlink(None, 'new target')
660
757
self.assertEqual("=== target is 'new target'\n",
661
758
differ.to_file.getvalue())
663
differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
760
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
664
761
differ.diff_symlink('old target', 'new target')
665
762
self.assertEqual("=== target changed 'old target' => 'new target'\n",
666
763
differ.to_file.getvalue())
1242
1339
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1244
_test_needs_features = [compiled_patiencediff_feature]
1341
_test_needs_features = [features.compiled_patiencediff_feature]
1246
1343
def setUp(self):
1247
1344
super(TestPatienceDiffLibFiles_c, self).setUp()
1248
import bzrlib._patiencediff_c
1345
from bzrlib import _patiencediff_c
1249
1346
self._PatienceSequenceMatcher = \
1250
bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1253
class TestUsingCompiledIfAvailable(TestCase):
1347
_patiencediff_c.PatienceSequenceMatcher_c
1350
class TestUsingCompiledIfAvailable(tests.TestCase):
1255
1352
def test_PatienceSequenceMatcher(self):
1256
if compiled_patiencediff_feature.available():
1353
if features.compiled_patiencediff_feature.available():
1257
1354
from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1258
1355
self.assertIs(PatienceSequenceMatcher_c,
1259
bzrlib.patiencediff.PatienceSequenceMatcher)
1356
patiencediff.PatienceSequenceMatcher)
1261
1358
from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1262
1359
self.assertIs(PatienceSequenceMatcher_py,
1263
bzrlib.patiencediff.PatienceSequenceMatcher)
1360
patiencediff.PatienceSequenceMatcher)
1265
1362
def test_unique_lcs(self):
1266
if compiled_patiencediff_feature.available():
1363
if features.compiled_patiencediff_feature.available():
1267
1364
from bzrlib._patiencediff_c import unique_lcs_c
1268
1365
self.assertIs(unique_lcs_c,
1269
bzrlib.patiencediff.unique_lcs)
1366
patiencediff.unique_lcs)
1271
1368
from bzrlib._patiencediff_py import unique_lcs_py
1272
1369
self.assertIs(unique_lcs_py,
1273
bzrlib.patiencediff.unique_lcs)
1370
patiencediff.unique_lcs)
1275
1372
def test_recurse_matches(self):
1276
if compiled_patiencediff_feature.available():
1373
if features.compiled_patiencediff_feature.available():
1277
1374
from bzrlib._patiencediff_c import recurse_matches_c
1278
1375
self.assertIs(recurse_matches_c,
1279
bzrlib.patiencediff.recurse_matches)
1376
patiencediff.recurse_matches)
1281
1378
from bzrlib._patiencediff_py import recurse_matches_py
1282
1379
self.assertIs(recurse_matches_py,
1283
bzrlib.patiencediff.recurse_matches)
1286
class TestDiffFromTool(TestCaseWithTransport):
1380
patiencediff.recurse_matches)
1383
class TestDiffFromTool(tests.TestCaseWithTransport):
1288
1385
def test_from_string(self):
1289
diff_obj = DiffFromTool.from_string('diff', None, None, None)
1386
diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1290
1387
self.addCleanup(diff_obj.finish)
1291
1388
self.assertEqual(['diff', '@old_path', '@new_path'],
1292
1389
diff_obj.command_template)
1294
1391
def test_from_string_u5(self):
1295
diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1392
diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1296
1394
self.addCleanup(diff_obj.finish)
1297
1395
self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1298
1396
diff_obj.command_template)
1299
1397
self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1300
1398
diff_obj._get_command('old-path', 'new-path'))
1400
def test_from_string_path_with_backslashes(self):
1401
self.requireFeature(features.backslashdir_feature)
1402
tool = 'C:\\Tools\\Diff.exe'
1403
diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1404
self.addCleanup(diff_obj.finish)
1405
self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
1406
diff_obj.command_template)
1407
self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1408
diff_obj._get_command('old-path', 'new-path'))
1302
1410
def test_execute(self):
1303
1411
output = StringIO()
1304
diff_obj = DiffFromTool(['python', '-c',
1305
'print "@old_path @new_path"'],
1412
diff_obj = diff.DiffFromTool(['python', '-c',
1413
'print "@old_path @new_path"'],
1307
1415
self.addCleanup(diff_obj.finish)
1308
1416
diff_obj._execute('old', 'new')
1309
1417
self.assertEqual(output.getvalue().rstrip(), 'old new')
1311
def test_excute_missing(self):
1312
diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1419
def test_execute_missing(self):
1420
diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1314
1422
self.addCleanup(diff_obj.finish)
1315
e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1423
e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1317
1425
self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1318
1426
' on this machine', str(e))
1320
1428
def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1321
self.requireFeature(AttribFeature)
1429
self.requireFeature(features.AttribFeature)
1322
1430
output = StringIO()
1323
1431
tree = self.make_branch_and_tree('tree')
1324
1432
self.build_tree_contents([('tree/file', 'content')])
1364
1473
self.addCleanup(old_tree.unlock)
1365
1474
tree.lock_read()
1366
1475
self.addCleanup(tree.unlock)
1367
diff_obj = DiffFromTool(['python', '-c',
1368
'print "@old_path @new_path"'],
1369
old_tree, tree, output)
1476
diff_obj = diff.DiffFromTool(['python', '-c',
1477
'print "@old_path @new_path"'],
1478
old_tree, tree, output)
1370
1479
self.addCleanup(diff_obj.finish)
1371
1480
self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1372
1481
old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1374
1483
self.assertContainsRe(old_path, 'old/oldname$')
1375
self.assertEqual(0, os.stat(old_path).st_mtime)
1484
self.assertEqual(315532800, os.stat(old_path).st_mtime)
1376
1485
self.assertContainsRe(new_path, 'tree/newname$')
1377
1486
self.assertFileEqual('oldcontent', old_path)
1378
1487
self.assertFileEqual('newcontent', new_path)
1382
1491
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1385
class TestGetTreesAndBranchesToDiff(TestCaseWithTransport):
1494
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1496
def test_encodable_filename(self):
1497
# Just checks file path for external diff tool.
1498
# We cannot change CPython's internal encoding used by os.exec*.
1499
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1501
for _, scenario in EncodingAdapter.encoding_scenarios:
1502
encoding = scenario['encoding']
1503
dirname = scenario['info']['directory']
1504
filename = scenario['info']['filename']
1506
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1507
relpath = dirname + u'/' + filename
1508
fullpath = diffobj._safe_filename('safe', relpath)
1511
fullpath.encode(encoding).decode(encoding)
1513
self.assert_(fullpath.startswith(diffobj._root + '/safe'))
1515
def test_unencodable_filename(self):
1516
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1518
for _, scenario in EncodingAdapter.encoding_scenarios:
1519
encoding = scenario['encoding']
1520
dirname = scenario['info']['directory']
1521
filename = scenario['info']['filename']
1523
if encoding == 'iso-8859-1':
1524
encoding = 'iso-8859-2'
1526
encoding = 'iso-8859-1'
1528
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1529
relpath = dirname + u'/' + filename
1530
fullpath = diffobj._safe_filename('safe', relpath)
1533
fullpath.encode(encoding).decode(encoding)
1535
self.assert_(fullpath.startswith(diffobj._root + '/safe'))
1538
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1540
def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1541
"""Call get_trees_and_branches_to_diff_locked."""
1542
return diff.get_trees_and_branches_to_diff_locked(
1543
path_list, revision_specs, old_url, new_url, self.addCleanup)
1387
1545
def test_basic(self):
1388
1546
tree = self.make_branch_and_tree('tree')
1389
1547
(old_tree, new_tree,
1390
1548
old_branch, new_branch,
1391
specific_files, extra_trees) = \
1392
get_trees_and_branches_to_diff(['tree'], None, None, None)
1549
specific_files, extra_trees) = self.call_gtabtd(
1550
['tree'], None, None, None)
1394
self.assertIsInstance(old_tree, RevisionTree)
1395
#print dir (old_tree)
1396
self.assertEqual(_mod_revision.NULL_REVISION, old_tree.get_revision_id())
1552
self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1553
self.assertEqual(_mod_revision.NULL_REVISION,
1554
old_tree.get_revision_id())
1397
1555
self.assertEqual(tree.basedir, new_tree.basedir)
1398
1556
self.assertEqual(tree.branch.base, old_branch.base)
1399
1557
self.assertEqual(tree.branch.base, new_branch.base)
1408
1566
self.build_tree_contents([('tree/file', 'newcontent')])
1409
1567
tree.commit('new tree', timestamp=0, rev_id="new-id")
1411
revisions = [RevisionSpec.from_string('1'),
1412
RevisionSpec.from_string('2')]
1569
revisions = [revisionspec.RevisionSpec.from_string('1'),
1570
revisionspec.RevisionSpec.from_string('2')]
1413
1571
(old_tree, new_tree,
1414
1572
old_branch, new_branch,
1415
specific_files, extra_trees) = \
1416
get_trees_and_branches_to_diff(['tree'], revisions, None, None)
1573
specific_files, extra_trees) = self.call_gtabtd(
1574
['tree'], revisions, None, None)
1418
self.assertIsInstance(old_tree, RevisionTree)
1576
self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1419
1577
self.assertEqual("old-id", old_tree.get_revision_id())
1420
self.assertIsInstance(new_tree, RevisionTree)
1578
self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1421
1579
self.assertEqual("new-id", new_tree.get_revision_id())
1422
1580
self.assertEqual(tree.branch.base, old_branch.base)
1423
1581
self.assertEqual(tree.branch.base, new_branch.base)