~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

(vila) Revise legal option names to be less drastic. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2014 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
import os
18
18
from cStringIO import StringIO
19
19
import subprocess
20
 
import sys
21
20
import tempfile
22
21
 
23
22
from bzrlib import (
32
31
    tests,
33
32
    transform,
34
33
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import features
37
 
 
38
 
 
39
 
class _AttribFeature(tests.Feature):
40
 
 
41
 
    def _probe(self):
42
 
        if (sys.platform not in ('cygwin', 'win32')):
43
 
            return False
44
 
        try:
45
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
46
 
        except OSError, e:
47
 
            return False
48
 
        return (0 == proc.wait())
49
 
 
50
 
    def feature_name(self):
51
 
        return 'attrib Windows command-line tool'
52
 
 
53
 
AttribFeature = _AttribFeature()
54
 
 
55
 
 
56
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
57
 
                                    'bzrlib._patiencediff_c')
 
34
from bzrlib.tests import (
 
35
    features,
 
36
    EncodingAdapter,
 
37
)
 
38
from bzrlib.tests.blackbox.test_diff import subst_dates
 
39
from bzrlib.tests.scenarios import load_tests_apply_scenarios
 
40
 
 
41
 
 
42
load_tests = load_tests_apply_scenarios
58
43
 
59
44
 
60
45
def udiff_lines(old, new, allow_binary=False):
80
65
    return lines
81
66
 
82
67
 
 
68
class TestDiffOptions(tests.TestCase):
 
69
 
 
70
    def test_unified_added(self):
 
71
        """Check for default style '-u' only if no other style specified
 
72
        in 'diff-options'.
 
73
        """
 
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']))
 
77
 
 
78
 
 
79
class TestDiffOptionsScenarios(tests.TestCase):
 
80
 
 
81
    scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
 
82
    style = None # Set by load_tests_apply_scenarios from scenarios
 
83
 
 
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)
 
89
 
 
90
 
83
91
class TestDiff(tests.TestCase):
84
92
 
85
93
    def test_add_nl(self):
143
151
        self.check_patch(lines)
144
152
 
145
153
    def test_external_diff_binary_lang_c(self):
146
 
        old_env = {}
147
154
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
149
 
        try:
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'])
156
 
        finally:
157
 
            for lang, old_val in old_env.iteritems():
158
 
                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'])
159
161
 
160
162
    def test_no_external_diff(self):
161
163
        """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']
164
 
        try:
165
 
            os.environ['PATH'] = ''
166
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
167
 
                              'old', ['boo\n'], 'new', ['goo\n'],
168
 
                              StringIO(), diff_opts=['-u'])
169
 
        finally:
170
 
            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'])
171
170
 
172
171
    def test_internal_diff_default(self):
173
172
        # Default internal diff encoding is utf8
234
233
        output = StringIO.StringIO()
235
234
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
235
                            u'new_\xe5', ['new_text\n'], output)
237
 
        self.failUnless(isinstance(output.getvalue(), str),
 
236
        self.assertIsInstance(output.getvalue(), str,
238
237
            'internal_diff should return bytestrings')
239
238
 
 
239
    def test_internal_diff_default_context(self):
 
240
        output = StringIO()
 
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',
 
248
                           '+++ new\n',
 
249
                           '@@ -3,4 +3,4 @@\n',
 
250
                           ' same_text\n',
 
251
                           ' same_text\n',
 
252
                           ' same_text\n',
 
253
                           '-old_text\n',
 
254
                           '+new_text\n',
 
255
                           '\n',
 
256
                          ]
 
257
                          , lines)
 
258
 
 
259
    def test_internal_diff_no_context(self):
 
260
        output = StringIO()
 
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,
 
265
                           context_lines=0)
 
266
        lines = output.getvalue().splitlines(True)
 
267
        self.check_patch(lines)
 
268
        self.assertEquals(['--- old\n',
 
269
                           '+++ new\n',
 
270
                           '@@ -6,1 +6,1 @@\n',
 
271
                           '-old_text\n',
 
272
                           '+new_text\n',
 
273
                           '\n',
 
274
                          ]
 
275
                          , lines)
 
276
 
 
277
    def test_internal_diff_more_context(self):
 
278
        output = StringIO()
 
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,
 
283
                           context_lines=4)
 
284
        lines = output.getvalue().splitlines(True)
 
285
        self.check_patch(lines)
 
286
        self.assertEquals(['--- old\n',
 
287
                           '+++ new\n',
 
288
                           '@@ -2,5 +2,5 @@\n',
 
289
                           ' same_text\n',
 
290
                           ' same_text\n',
 
291
                           ' same_text\n',
 
292
                           ' same_text\n',
 
293
                           '-old_text\n',
 
294
                           '+new_text\n',
 
295
                           '\n',
 
296
                          ]
 
297
                          , lines)
 
298
 
 
299
 
 
300
 
 
301
 
240
302
 
241
303
class TestDiffFiles(tests.TestCaseInTempDir):
242
304
 
246
308
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
247
309
 
248
310
        cmd = ['diff', '-u', '--binary', 'old', 'new']
249
 
        open('old', 'wb').write('\x00foobar\n')
250
 
        open('new', 'wb').write('foo\x00bar\n')
 
311
        with open('old', 'wb') as f: f.write('\x00foobar\n')
 
312
        with open('new', 'wb') as f: f.write('foo\x00bar\n')
251
313
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
314
                                     stdin=subprocess.PIPE)
253
315
        out, err = pipe.communicate()
257
319
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
258
320
 
259
321
 
260
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
261
 
    """Has a helper for running show_diff_trees"""
262
 
 
263
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
264
 
        output = StringIO()
265
 
        if working_tree is not None:
266
 
            extra_trees = (working_tree,)
267
 
        else:
268
 
            extra_trees = ()
269
 
        diff.show_diff_trees(tree1, tree2, output,
270
 
                             specific_files=specific_files,
271
 
                             extra_trees=extra_trees, old_label='old/',
272
 
                             new_label='new/')
273
 
        return output.getvalue()
274
 
 
275
 
 
276
 
class TestDiffDates(TestShowDiffTreesHelper):
 
322
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
323
    output = StringIO()
 
324
    if working_tree is not None:
 
325
        extra_trees = (working_tree,)
 
326
    else:
 
327
        extra_trees = ()
 
328
    diff.show_diff_trees(tree1, tree2, output,
 
329
        specific_files=specific_files,
 
330
        extra_trees=extra_trees, old_label='old/',
 
331
        new_label='new/')
 
332
    return output.getvalue()
 
333
 
 
334
 
 
335
class TestDiffDates(tests.TestCaseWithTransport):
277
336
 
278
337
    def setUp(self):
279
338
        super(TestDiffDates, self).setUp()
314
373
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
315
374
 
316
375
    def test_diff_rev_tree_working_tree(self):
317
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
376
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
377
        # note that the date for old/file1 is from rev 2 rather than from
319
378
        # the basis revision (rev 4)
320
379
        self.assertEqualDiff(output, '''\
330
389
    def test_diff_rev_tree_rev_tree(self):
331
390
        tree1 = self.b.repository.revision_tree('rev-2')
332
391
        tree2 = self.b.repository.revision_tree('rev-3')
333
 
        output = self.get_diff(tree1, tree2)
 
392
        output = get_diff_as_string(tree1, tree2)
334
393
        self.assertEqualDiff(output, '''\
335
394
=== modified file 'file2'
336
395
--- old/file2\t2006-04-01 00:00:00 +0000
344
403
    def test_diff_add_files(self):
345
404
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
346
405
        tree2 = self.b.repository.revision_tree('rev-1')
347
 
        output = self.get_diff(tree1, tree2)
 
406
        output = get_diff_as_string(tree1, tree2)
348
407
        # the files have the epoch time stamp for the tree in which
349
408
        # they don't exist.
350
409
        self.assertEqualDiff(output, '''\
365
424
    def test_diff_remove_files(self):
366
425
        tree1 = self.b.repository.revision_tree('rev-3')
367
426
        tree2 = self.b.repository.revision_tree('rev-4')
368
 
        output = self.get_diff(tree1, tree2)
 
427
        output = get_diff_as_string(tree1, tree2)
369
428
        # the file has the epoch time stamp for the tree in which
370
429
        # it doesn't exist.
371
430
        self.assertEqualDiff(output, '''\
382
441
        self.wt.rename_one('file1', 'file1b')
383
442
        old_tree = self.b.repository.revision_tree('rev-1')
384
443
        new_tree = self.b.repository.revision_tree('rev-4')
385
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
444
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
386
445
                            working_tree=self.wt)
387
446
        self.assertContainsRe(out, 'file1\t')
388
447
 
394
453
        self.wt.rename_one('file1', 'dir1/file1')
395
454
        old_tree = self.b.repository.revision_tree('rev-1')
396
455
        new_tree = self.b.repository.revision_tree('rev-4')
397
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
456
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
398
457
                            working_tree=self.wt)
399
458
        self.assertContainsRe(out, 'file1\t')
400
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
459
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
401
460
                            working_tree=self.wt)
402
461
        self.assertNotContainsRe(out, 'file1\t')
403
462
 
404
463
 
405
 
 
406
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
464
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
465
    """Direct tests for show_diff_trees"""
408
466
 
409
467
    def test_modified_file(self):
414
472
        tree.commit('one', rev_id='rev-1')
415
473
 
416
474
        self.build_tree_contents([('tree/file', 'new contents\n')])
417
 
        d = self.get_diff(tree.basis_tree(), tree)
 
475
        d = get_diff_as_string(tree.basis_tree(), tree)
418
476
        self.assertContainsRe(d, "=== modified file 'file'\n")
419
477
        self.assertContainsRe(d, '--- old/file\t')
420
478
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
431
489
 
432
490
        tree.rename_one('dir', 'other')
433
491
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
434
 
        d = self.get_diff(tree.basis_tree(), tree)
 
492
        d = get_diff_as_string(tree.basis_tree(), tree)
435
493
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
436
494
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
437
495
        # XXX: This is technically incorrect, because it used to be at another
450
508
        tree.commit('one', rev_id='rev-1')
451
509
 
452
510
        tree.rename_one('dir', 'newdir')
453
 
        d = self.get_diff(tree.basis_tree(), tree)
 
511
        d = get_diff_as_string(tree.basis_tree(), tree)
454
512
        # Renaming a directory should be a single "you renamed this dir" even
455
513
        # when there are files inside.
456
514
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
463
521
        tree.commit('one', rev_id='rev-1')
464
522
 
465
523
        tree.rename_one('file', 'newname')
466
 
        d = self.get_diff(tree.basis_tree(), tree)
 
524
        d = get_diff_as_string(tree.basis_tree(), tree)
467
525
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
468
526
        # We shouldn't have a --- or +++ line, because there is no content
469
527
        # change
478
536
 
479
537
        tree.rename_one('file', 'newname')
480
538
        self.build_tree_contents([('tree/newname', 'new contents\n')])
481
 
        d = self.get_diff(tree.basis_tree(), tree)
 
539
        d = get_diff_as_string(tree.basis_tree(), tree)
482
540
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
541
        self.assertContainsRe(d, '--- old/file\t')
484
542
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
508
566
        tree.rename_one('c', 'new-c')
509
567
        tree.rename_one('d', 'new-d')
510
568
 
511
 
        d = self.get_diff(tree.basis_tree(), tree)
 
569
        d = get_diff_as_string(tree.basis_tree(), tree)
512
570
 
513
571
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
514
572
                                  ".*\+x to -x.*\)")
521
579
        self.assertNotContainsRe(d, r"file 'e'")
522
580
        self.assertNotContainsRe(d, r"file 'f'")
523
581
 
524
 
 
525
582
    def test_binary_unicode_filenames(self):
526
583
        """Test that contents of files are *not* encoded in UTF-8 when there
527
584
        is a binary file in the diff.
528
585
        """
529
586
        # See https://bugs.launchpad.net/bugs/110092.
530
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
587
        self.requireFeature(features.UnicodeFilenameFeature)
531
588
 
532
589
        # This bug isn't triggered with cStringIO.
533
590
        from StringIO import StringIO
552
609
 
553
610
    def test_unicode_filename(self):
554
611
        """Test when the filename are unicode."""
555
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
612
        self.requireFeature(features.UnicodeFilenameFeature)
556
613
 
557
614
        alpha, omega = u'\u03b1', u'\u03c9'
558
615
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
573
630
        tree.add(['add_'+alpha], ['file-id'])
574
631
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
575
632
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
 
633
        d = get_diff_as_string(tree.basis_tree(), tree)
577
634
        self.assertContainsRe(d,
578
635
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
636
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
637
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
638
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
582
639
 
 
640
    def test_unicode_filename_path_encoding(self):
 
641
        """Test for bug #382699: unicode filenames on Windows should be shown
 
642
        in user encoding.
 
643
        """
 
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'
 
650
 
 
651
        tree = self.make_branch_and_tree('.')
 
652
        self.build_tree_contents([
 
653
            (test_txt, 'foo\n'),
 
654
            (u1234, 'foo\n'),
 
655
            (directory, None),
 
656
            ])
 
657
        tree.add([test_txt, u1234, directory])
 
658
 
 
659
        sio = StringIO()
 
660
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
661
            path_encoding='cp1251')
 
662
 
 
663
        output = subst_dates(sio.getvalue())
 
664
        shouldbe = ('''\
 
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
 
669
@@ -0,0 +1,1 @@
 
670
+foo
 
671
 
 
672
=== added file '?.txt'
 
673
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
674
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
675
@@ -0,0 +1,1 @@
 
676
+foo
 
677
 
 
678
''' % {'directory': _russian_test.encode('cp1251'),
 
679
       'test_txt': test_txt.encode('cp1251'),
 
680
      })
 
681
        self.assertEqualDiff(output, shouldbe)
 
682
 
583
683
 
584
684
class DiffWasIs(diff.DiffPath):
585
685
 
678
778
             ' \@\@\n-old\n\+new\n\n')
679
779
 
680
780
    def test_diff_kind_change(self):
681
 
        self.requireFeature(tests.SymlinkFeature)
 
781
        self.requireFeature(features.SymlinkFeature)
682
782
        self.build_tree_contents([('old-tree/olddir/',),
683
783
                                  ('old-tree/olddir/oldfile', 'old\n')])
684
784
        self.old_tree.add('olddir')
1142
1242
 
1143
1243
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1144
1244
 
1145
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1245
    _test_needs_features = [features.compiled_patiencediff_feature]
1146
1246
 
1147
1247
    def setUp(self):
1148
1248
        super(TestPatienceDiffLib_c, self).setUp()
1179
1279
                 'how are you today?\n']
1180
1280
        txt_b = ['hello there\n',
1181
1281
                 'how are you today?\n']
1182
 
        open('a1', 'wb').writelines(txt_a)
1183
 
        open('b1', 'wb').writelines(txt_b)
 
1282
        with open('a1', 'wb') as f: f.writelines(txt_a)
 
1283
        with open('b1', 'wb') as f: f.writelines(txt_b)
1184
1284
 
1185
1285
        unified_diff_files = patiencediff.unified_diff_files
1186
1286
        psm = self._PatienceSequenceMatcher
1196
1296
 
1197
1297
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1198
1298
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1199
 
        open('a2', 'wb').writelines(txt_a)
1200
 
        open('b2', 'wb').writelines(txt_b)
 
1299
        with open('a2', 'wb') as f: f.writelines(txt_a)
 
1300
        with open('b2', 'wb') as f: f.writelines(txt_b)
1201
1301
 
1202
1302
        # This is the result with LongestCommonSubstring matching
1203
1303
        self.assertEquals(['--- a2\n',
1238
1338
 
1239
1339
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1240
1340
 
1241
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1341
    _test_needs_features = [features.compiled_patiencediff_feature]
1242
1342
 
1243
1343
    def setUp(self):
1244
1344
        super(TestPatienceDiffLibFiles_c, self).setUp()
1250
1350
class TestUsingCompiledIfAvailable(tests.TestCase):
1251
1351
 
1252
1352
    def test_PatienceSequenceMatcher(self):
1253
 
        if compiled_patiencediff_feature.available():
 
1353
        if features.compiled_patiencediff_feature.available():
1254
1354
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1255
1355
            self.assertIs(PatienceSequenceMatcher_c,
1256
1356
                          patiencediff.PatienceSequenceMatcher)
1260
1360
                          patiencediff.PatienceSequenceMatcher)
1261
1361
 
1262
1362
    def test_unique_lcs(self):
1263
 
        if compiled_patiencediff_feature.available():
 
1363
        if features.compiled_patiencediff_feature.available():
1264
1364
            from bzrlib._patiencediff_c import unique_lcs_c
1265
1365
            self.assertIs(unique_lcs_c,
1266
1366
                          patiencediff.unique_lcs)
1270
1370
                          patiencediff.unique_lcs)
1271
1371
 
1272
1372
    def test_recurse_matches(self):
1273
 
        if compiled_patiencediff_feature.available():
 
1373
        if features.compiled_patiencediff_feature.available():
1274
1374
            from bzrlib._patiencediff_c import recurse_matches_c
1275
1375
            self.assertIs(recurse_matches_c,
1276
1376
                          patiencediff.recurse_matches)
1316
1416
        diff_obj._execute('old', 'new')
1317
1417
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1318
1418
 
1319
 
    def test_excute_missing(self):
 
1419
    def test_execute_missing(self):
1320
1420
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
1421
                                     None, None, None)
1322
1422
        self.addCleanup(diff_obj.finish)
1326
1426
                         ' on this machine', str(e))
1327
1427
 
1328
1428
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1329
 
        self.requireFeature(AttribFeature)
 
1429
        self.requireFeature(features.AttribFeature)
1330
1430
        output = StringIO()
1331
1431
        tree = self.make_branch_and_tree('tree')
1332
1432
        self.build_tree_contents([('tree/file', 'content')])
1391
1491
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1392
1492
 
1393
1493
 
 
1494
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1495
 
 
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'],
 
1500
                                    None, None, None)
 
1501
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1502
            encoding = scenario['encoding']
 
1503
            dirname  = scenario['info']['directory']
 
1504
            filename = scenario['info']['filename']
 
1505
 
 
1506
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1507
            relpath = dirname + u'/' + filename
 
1508
            fullpath = diffobj._safe_filename('safe', relpath)
 
1509
            self.assertEqual(
 
1510
                    fullpath,
 
1511
                    fullpath.encode(encoding).decode(encoding)
 
1512
                    )
 
1513
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1514
 
 
1515
    def test_unencodable_filename(self):
 
1516
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1517
                                    None, None, None)
 
1518
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1519
            encoding = scenario['encoding']
 
1520
            dirname  = scenario['info']['directory']
 
1521
            filename = scenario['info']['filename']
 
1522
 
 
1523
            if encoding == 'iso-8859-1':
 
1524
                encoding = 'iso-8859-2'
 
1525
            else:
 
1526
                encoding = 'iso-8859-1'
 
1527
 
 
1528
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1529
            relpath = dirname + u'/' + filename
 
1530
            fullpath = diffobj._safe_filename('safe', relpath)
 
1531
            self.assertEqual(
 
1532
                    fullpath,
 
1533
                    fullpath.encode(encoding).decode(encoding)
 
1534
                    )
 
1535
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1536
 
 
1537
 
1394
1538
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1395
1539
 
1396
1540
    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.
1399
 
        """
 
1541
        """Call get_trees_and_branches_to_diff_locked."""
1400
1542
        return diff.get_trees_and_branches_to_diff_locked(
1401
1543
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1402
1544
 
1439
1581
        self.assertEqual(tree.branch.base, new_branch.base)
1440
1582
        self.assertIs(None, specific_files)
1441
1583
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
1442
 
 
1443
 
 
1444
 
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
1445
 
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
1446
 
    deprecated get_trees_and_branches_to_diff function.
1447
 
    """
1448
 
 
1449
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1450
 
        return self.applyDeprecated(
1451
 
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
1452
 
            path_list, revision_specs, old_url, new_url)
1453