~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

(jelmer) Use the absolute_import feature everywhere in bzrlib,
 and add a source test to make sure it's used everywhere. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import os.path
19
18
from cStringIO import StringIO
20
 
import errno
21
19
import subprocess
22
20
import sys
23
 
from tempfile import TemporaryFile
24
 
 
25
 
from bzrlib import tests
26
 
from bzrlib.diff import (
27
 
    DiffFromTool,
28
 
    DiffPath,
29
 
    DiffSymlink,
30
 
    DiffTree,
31
 
    DiffText,
32
 
    external_diff,
33
 
    internal_diff,
34
 
    show_diff_trees,
35
 
    get_trees_and_branches_to_diff,
36
 
    )
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
47
 
 
48
 
 
49
 
class _AttribFeature(Feature):
50
 
 
51
 
    def _probe(self):
52
 
        if (sys.platform not in ('cygwin', 'win32')):
53
 
            return False
54
 
        try:
55
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
56
 
        except OSError, e:
57
 
            return False
58
 
        return (0 == proc.wait())
59
 
 
60
 
    def feature_name(self):
61
 
        return 'attrib Windows command-line tool'
62
 
 
63
 
AttribFeature = _AttribFeature()
64
 
 
65
 
 
66
 
class _CompiledPatienceDiffFeature(Feature):
67
 
 
68
 
    def _probe(self):
69
 
        try:
70
 
            import bzrlib._patiencediff_c
71
 
        except ImportError:
72
 
            return False
73
 
        return True
74
 
 
75
 
    def feature_name(self):
76
 
        return 'bzrlib._patiencediff_c'
77
 
 
78
 
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
21
import tempfile
 
22
 
 
23
from bzrlib import (
 
24
    diff,
 
25
    errors,
 
26
    osutils,
 
27
    patiencediff,
 
28
    _patiencediff_py,
 
29
    revision as _mod_revision,
 
30
    revisionspec,
 
31
    revisiontree,
 
32
    tests,
 
33
    transform,
 
34
    )
 
35
from bzrlib.symbol_versioning import deprecated_in
 
36
from bzrlib.tests import features, EncodingAdapter
 
37
from bzrlib.tests.blackbox.test_diff import subst_dates
 
38
from bzrlib.tests import (
 
39
    features,
 
40
    )
79
41
 
80
42
 
81
43
def udiff_lines(old, new, allow_binary=False):
82
44
    output = StringIO()
83
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
45
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
84
46
    output.seek(0, 0)
85
47
    return output.readlines()
86
48
 
90
52
        # StringIO has no fileno, so it tests a different codepath
91
53
        output = StringIO()
92
54
    else:
93
 
        output = TemporaryFile()
 
55
        output = tempfile.TemporaryFile()
94
56
    try:
95
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
96
 
    except NoDiff:
97
 
        raise TestSkipped('external "diff" not present to test')
 
57
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
58
    except errors.NoDiff:
 
59
        raise tests.TestSkipped('external "diff" not present to test')
98
60
    output.seek(0, 0)
99
61
    lines = output.readlines()
100
62
    output.close()
101
63
    return lines
102
64
 
103
65
 
104
 
class TestDiff(TestCase):
 
66
class TestDiff(tests.TestCase):
105
67
 
106
68
    def test_add_nl(self):
107
69
        """diff generates a valid diff for patches that add a newline"""
143
105
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
144
106
 
145
107
    def test_binary_lines(self):
146
 
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
147
 
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
148
 
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
149
 
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
108
        empty = []
 
109
        uni_lines = [1023 * 'a' + '\x00']
 
110
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
 
111
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
 
112
        udiff_lines(uni_lines , empty, allow_binary=True)
 
113
        udiff_lines(empty, uni_lines, allow_binary=True)
150
114
 
151
115
    def test_external_diff(self):
152
116
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
162
126
        self.check_patch(lines)
163
127
 
164
128
    def test_external_diff_binary_lang_c(self):
165
 
        old_env = {}
166
129
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
167
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
168
 
        try:
169
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
170
 
            # Older versions of diffutils say "Binary files", newer
171
 
            # versions just say "Files".
172
 
            self.assertContainsRe(lines[0],
173
 
                                  '(Binary f|F)iles old and new differ\n')
174
 
            self.assertEquals(lines[1:], ['\n'])
175
 
        finally:
176
 
            for lang, old_val in old_env.iteritems():
177
 
                osutils.set_or_unset_env(lang, old_val)
 
130
            self.overrideEnv(lang, 'C')
 
131
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
132
        # Older versions of diffutils say "Binary files", newer
 
133
        # versions just say "Files".
 
134
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
 
135
        self.assertEquals(lines[1:], ['\n'])
178
136
 
179
137
    def test_no_external_diff(self):
180
138
        """Check that NoDiff is raised when diff is not available"""
181
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
182
 
        orig_path = os.environ['PATH']
183
 
        try:
184
 
            os.environ['PATH'] = ''
185
 
            self.assertRaises(NoDiff, external_diff,
186
 
                              'old', ['boo\n'], 'new', ['goo\n'],
187
 
                              StringIO(), diff_opts=['-u'])
188
 
        finally:
189
 
            os.environ['PATH'] = orig_path
 
139
        # Make sure no 'diff' command is available
 
140
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
 
141
        self.overrideEnv('PATH', '')
 
142
        self.assertRaises(errors.NoDiff, diff.external_diff,
 
143
                          'old', ['boo\n'], 'new', ['goo\n'],
 
144
                          StringIO(), diff_opts=['-u'])
190
145
 
191
146
    def test_internal_diff_default(self):
192
147
        # Default internal diff encoding is utf8
193
148
        output = StringIO()
194
 
        internal_diff(u'old_\xb5', ['old_text\n'],
195
 
                    u'new_\xe5', ['new_text\n'], output)
 
149
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
150
                           u'new_\xe5', ['new_text\n'], output)
196
151
        lines = output.getvalue().splitlines(True)
197
152
        self.check_patch(lines)
198
153
        self.assertEquals(['--- old_\xc2\xb5\n',
206
161
 
207
162
    def test_internal_diff_utf8(self):
208
163
        output = StringIO()
209
 
        internal_diff(u'old_\xb5', ['old_text\n'],
210
 
                    u'new_\xe5', ['new_text\n'], output,
211
 
                    path_encoding='utf8')
 
164
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
165
                           u'new_\xe5', ['new_text\n'], output,
 
166
                           path_encoding='utf8')
212
167
        lines = output.getvalue().splitlines(True)
213
168
        self.check_patch(lines)
214
169
        self.assertEquals(['--- old_\xc2\xb5\n',
222
177
 
223
178
    def test_internal_diff_iso_8859_1(self):
224
179
        output = StringIO()
225
 
        internal_diff(u'old_\xb5', ['old_text\n'],
226
 
                    u'new_\xe5', ['new_text\n'], output,
227
 
                    path_encoding='iso-8859-1')
 
180
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
181
                           u'new_\xe5', ['new_text\n'], output,
 
182
                           path_encoding='iso-8859-1')
228
183
        lines = output.getvalue().splitlines(True)
229
184
        self.check_patch(lines)
230
185
        self.assertEquals(['--- old_\xb5\n',
238
193
 
239
194
    def test_internal_diff_no_content(self):
240
195
        output = StringIO()
241
 
        internal_diff(u'old', [], u'new', [], output)
 
196
        diff.internal_diff(u'old', [], u'new', [], output)
242
197
        self.assertEqual('', output.getvalue())
243
198
 
244
199
    def test_internal_diff_no_changes(self):
245
200
        output = StringIO()
246
 
        internal_diff(u'old', ['text\n', 'contents\n'],
247
 
                      u'new', ['text\n', 'contents\n'],
248
 
                      output)
 
201
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
202
                           u'new', ['text\n', 'contents\n'],
 
203
                           output)
249
204
        self.assertEqual('', output.getvalue())
250
205
 
251
206
    def test_internal_diff_returns_bytes(self):
252
207
        import StringIO
253
208
        output = StringIO.StringIO()
254
 
        internal_diff(u'old_\xb5', ['old_text\n'],
255
 
                    u'new_\xe5', ['new_text\n'], output)
256
 
        self.failUnless(isinstance(output.getvalue(), str),
 
209
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
210
                            u'new_\xe5', ['new_text\n'], output)
 
211
        self.assertIsInstance(output.getvalue(), str,
257
212
            'internal_diff should return bytestrings')
258
213
 
259
214
 
260
 
class TestDiffFiles(TestCaseInTempDir):
 
215
class TestDiffFiles(tests.TestCaseInTempDir):
261
216
 
262
217
    def test_external_diff_binary(self):
263
218
        """The output when using external diff should use diff's i18n error"""
276
231
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
277
232
 
278
233
 
279
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
280
 
    """Has a helper for running show_diff_trees"""
281
 
 
282
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
283
 
        output = StringIO()
284
 
        if working_tree is not None:
285
 
            extra_trees = (working_tree,)
286
 
        else:
287
 
            extra_trees = ()
288
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
289
 
                        extra_trees=extra_trees, old_label='old/',
290
 
                        new_label='new/')
291
 
        return output.getvalue()
292
 
 
293
 
 
294
 
class TestDiffDates(TestShowDiffTreesHelper):
 
234
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
235
    output = StringIO()
 
236
    if working_tree is not None:
 
237
        extra_trees = (working_tree,)
 
238
    else:
 
239
        extra_trees = ()
 
240
    diff.show_diff_trees(tree1, tree2, output,
 
241
        specific_files=specific_files,
 
242
        extra_trees=extra_trees, old_label='old/',
 
243
        new_label='new/')
 
244
    return output.getvalue()
 
245
 
 
246
 
 
247
class TestDiffDates(tests.TestCaseWithTransport):
295
248
 
296
249
    def setUp(self):
297
250
        super(TestDiffDates, self).setUp()
332
285
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
333
286
 
334
287
    def test_diff_rev_tree_working_tree(self):
335
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
288
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
336
289
        # note that the date for old/file1 is from rev 2 rather than from
337
290
        # the basis revision (rev 4)
338
291
        self.assertEqualDiff(output, '''\
348
301
    def test_diff_rev_tree_rev_tree(self):
349
302
        tree1 = self.b.repository.revision_tree('rev-2')
350
303
        tree2 = self.b.repository.revision_tree('rev-3')
351
 
        output = self.get_diff(tree1, tree2)
 
304
        output = get_diff_as_string(tree1, tree2)
352
305
        self.assertEqualDiff(output, '''\
353
306
=== modified file 'file2'
354
307
--- old/file2\t2006-04-01 00:00:00 +0000
362
315
    def test_diff_add_files(self):
363
316
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
364
317
        tree2 = self.b.repository.revision_tree('rev-1')
365
 
        output = self.get_diff(tree1, tree2)
 
318
        output = get_diff_as_string(tree1, tree2)
366
319
        # the files have the epoch time stamp for the tree in which
367
320
        # they don't exist.
368
321
        self.assertEqualDiff(output, '''\
383
336
    def test_diff_remove_files(self):
384
337
        tree1 = self.b.repository.revision_tree('rev-3')
385
338
        tree2 = self.b.repository.revision_tree('rev-4')
386
 
        output = self.get_diff(tree1, tree2)
 
339
        output = get_diff_as_string(tree1, tree2)
387
340
        # the file has the epoch time stamp for the tree in which
388
341
        # it doesn't exist.
389
342
        self.assertEqualDiff(output, '''\
400
353
        self.wt.rename_one('file1', 'file1b')
401
354
        old_tree = self.b.repository.revision_tree('rev-1')
402
355
        new_tree = self.b.repository.revision_tree('rev-4')
403
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
356
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
404
357
                            working_tree=self.wt)
405
358
        self.assertContainsRe(out, 'file1\t')
406
359
 
412
365
        self.wt.rename_one('file1', 'dir1/file1')
413
366
        old_tree = self.b.repository.revision_tree('rev-1')
414
367
        new_tree = self.b.repository.revision_tree('rev-4')
415
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
368
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
416
369
                            working_tree=self.wt)
417
370
        self.assertContainsRe(out, 'file1\t')
418
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
371
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
419
372
                            working_tree=self.wt)
420
373
        self.assertNotContainsRe(out, 'file1\t')
421
374
 
422
375
 
423
 
 
424
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
376
class TestShowDiffTrees(tests.TestCaseWithTransport):
425
377
    """Direct tests for show_diff_trees"""
426
378
 
427
379
    def test_modified_file(self):
432
384
        tree.commit('one', rev_id='rev-1')
433
385
 
434
386
        self.build_tree_contents([('tree/file', 'new contents\n')])
435
 
        diff = self.get_diff(tree.basis_tree(), tree)
436
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
437
 
        self.assertContainsRe(diff, '--- old/file\t')
438
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
439
 
        self.assertContainsRe(diff, '-contents\n'
440
 
                                    '\\+new contents\n')
 
387
        d = get_diff_as_string(tree.basis_tree(), tree)
 
388
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
389
        self.assertContainsRe(d, '--- old/file\t')
 
390
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
391
        self.assertContainsRe(d, '-contents\n'
 
392
                                 '\\+new contents\n')
441
393
 
442
394
    def test_modified_file_in_renamed_dir(self):
443
395
        """Test when a file is modified in a renamed directory."""
449
401
 
450
402
        tree.rename_one('dir', 'other')
451
403
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
452
 
        diff = self.get_diff(tree.basis_tree(), tree)
453
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
454
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
 
404
        d = get_diff_as_string(tree.basis_tree(), tree)
 
405
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
406
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
455
407
        # XXX: This is technically incorrect, because it used to be at another
456
408
        # location. What to do?
457
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
458
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
459
 
        self.assertContainsRe(diff, '-contents\n'
460
 
                                    '\\+new contents\n')
 
409
        self.assertContainsRe(d, '--- old/dir/file\t')
 
410
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
411
        self.assertContainsRe(d, '-contents\n'
 
412
                                 '\\+new contents\n')
461
413
 
462
414
    def test_renamed_directory(self):
463
415
        """Test when only a directory is only renamed."""
468
420
        tree.commit('one', rev_id='rev-1')
469
421
 
470
422
        tree.rename_one('dir', 'newdir')
471
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
423
        d = get_diff_as_string(tree.basis_tree(), tree)
472
424
        # Renaming a directory should be a single "you renamed this dir" even
473
425
        # when there are files inside.
474
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
426
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
475
427
 
476
428
    def test_renamed_file(self):
477
429
        """Test when a file is only renamed."""
481
433
        tree.commit('one', rev_id='rev-1')
482
434
 
483
435
        tree.rename_one('file', 'newname')
484
 
        diff = self.get_diff(tree.basis_tree(), tree)
485
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
436
        d = get_diff_as_string(tree.basis_tree(), tree)
 
437
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
486
438
        # We shouldn't have a --- or +++ line, because there is no content
487
439
        # change
488
 
        self.assertNotContainsRe(diff, '---')
 
440
        self.assertNotContainsRe(d, '---')
489
441
 
490
442
    def test_renamed_and_modified_file(self):
491
443
        """Test when a file is only renamed."""
496
448
 
497
449
        tree.rename_one('file', 'newname')
498
450
        self.build_tree_contents([('tree/newname', 'new contents\n')])
499
 
        diff = self.get_diff(tree.basis_tree(), tree)
500
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
501
 
        self.assertContainsRe(diff, '--- old/file\t')
502
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
503
 
        self.assertContainsRe(diff, '-contents\n'
504
 
                                    '\\+new contents\n')
 
451
        d = get_diff_as_string(tree.basis_tree(), tree)
 
452
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
453
        self.assertContainsRe(d, '--- old/file\t')
 
454
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
455
        self.assertContainsRe(d, '-contents\n'
 
456
                                 '\\+new contents\n')
505
457
 
506
458
 
507
459
    def test_internal_diff_exec_property(self):
526
478
        tree.rename_one('c', 'new-c')
527
479
        tree.rename_one('d', 'new-d')
528
480
 
529
 
        diff = self.get_diff(tree.basis_tree(), tree)
530
 
 
531
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
532
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
533
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
534
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
535
 
        self.assertNotContainsRe(diff, r"file 'e'")
536
 
        self.assertNotContainsRe(diff, r"file 'f'")
537
 
 
 
481
        d = get_diff_as_string(tree.basis_tree(), tree)
 
482
 
 
483
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
484
                                  ".*\+x to -x.*\)")
 
485
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
486
                                  ".*-x to \+x.*\)")
 
487
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
488
                                  ".*\+x to -x.*\)")
 
489
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
490
                                  ".*-x to \+x.*\)")
 
491
        self.assertNotContainsRe(d, r"file 'e'")
 
492
        self.assertNotContainsRe(d, r"file 'f'")
538
493
 
539
494
    def test_binary_unicode_filenames(self):
540
495
        """Test that contents of files are *not* encoded in UTF-8 when there
541
496
        is a binary file in the diff.
542
497
        """
543
498
        # See https://bugs.launchpad.net/bugs/110092.
544
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
499
        self.requireFeature(features.UnicodeFilenameFeature)
545
500
 
546
501
        # This bug isn't triggered with cStringIO.
547
502
        from StringIO import StringIO
555
510
        tree.add([alpha], ['file-id'])
556
511
        tree.add([omega], ['file-id-2'])
557
512
        diff_content = StringIO()
558
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
559
 
        diff = diff_content.getvalue()
560
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
561
 
        self.assertContainsRe(
562
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
563
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
564
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
565
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
 
513
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
 
514
        d = diff_content.getvalue()
 
515
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
 
516
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
517
                              % (alpha_utf8, alpha_utf8))
 
518
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
 
519
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
 
520
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
566
521
 
567
522
    def test_unicode_filename(self):
568
523
        """Test when the filename are unicode."""
569
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
524
        self.requireFeature(features.UnicodeFilenameFeature)
570
525
 
571
526
        alpha, omega = u'\u03b1', u'\u03c9'
572
527
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
587
542
        tree.add(['add_'+alpha], ['file-id'])
588
543
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
589
544
 
590
 
        diff = self.get_diff(tree.basis_tree(), tree)
591
 
        self.assertContainsRe(diff,
 
545
        d = get_diff_as_string(tree.basis_tree(), tree)
 
546
        self.assertContainsRe(d,
592
547
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
593
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
594
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
595
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
596
 
 
597
 
 
598
 
class DiffWasIs(DiffPath):
 
548
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
 
549
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
 
550
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
 
551
 
 
552
    def test_unicode_filename_path_encoding(self):
 
553
        """Test for bug #382699: unicode filenames on Windows should be shown
 
554
        in user encoding.
 
555
        """
 
556
        self.requireFeature(features.UnicodeFilenameFeature)
 
557
        # The word 'test' in Russian
 
558
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
559
        directory = _russian_test + u'/'
 
560
        test_txt = _russian_test + u'.txt'
 
561
        u1234 = u'\u1234.txt'
 
562
 
 
563
        tree = self.make_branch_and_tree('.')
 
564
        self.build_tree_contents([
 
565
            (test_txt, 'foo\n'),
 
566
            (u1234, 'foo\n'),
 
567
            (directory, None),
 
568
            ])
 
569
        tree.add([test_txt, u1234, directory])
 
570
 
 
571
        sio = StringIO()
 
572
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
573
            path_encoding='cp1251')
 
574
 
 
575
        output = subst_dates(sio.getvalue())
 
576
        shouldbe = ('''\
 
577
=== added directory '%(directory)s'
 
578
=== added file '%(test_txt)s'
 
579
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
580
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
581
@@ -0,0 +1,1 @@
 
582
+foo
 
583
 
 
584
=== added file '?.txt'
 
585
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
586
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
587
@@ -0,0 +1,1 @@
 
588
+foo
 
589
 
 
590
''' % {'directory': _russian_test.encode('cp1251'),
 
591
       'test_txt': test_txt.encode('cp1251'),
 
592
      })
 
593
        self.assertEqualDiff(output, shouldbe)
 
594
 
 
595
 
 
596
class DiffWasIs(diff.DiffPath):
599
597
 
600
598
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
601
599
        self.to_file.write('was: ')
605
603
        pass
606
604
 
607
605
 
608
 
class TestDiffTree(TestCaseWithTransport):
 
606
class TestDiffTree(tests.TestCaseWithTransport):
609
607
 
610
608
    def setUp(self):
611
 
        TestCaseWithTransport.setUp(self)
 
609
        super(TestDiffTree, self).setUp()
612
610
        self.old_tree = self.make_branch_and_tree('old-tree')
613
611
        self.old_tree.lock_write()
614
612
        self.addCleanup(self.old_tree.unlock)
615
613
        self.new_tree = self.make_branch_and_tree('new-tree')
616
614
        self.new_tree.lock_write()
617
615
        self.addCleanup(self.new_tree.unlock)
618
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
616
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
619
617
 
620
618
    def test_diff_text(self):
621
619
        self.build_tree_contents([('old-tree/olddir/',),
626
624
                                  ('new-tree/newdir/newfile', 'new\n')])
627
625
        self.new_tree.add('newdir')
628
626
        self.new_tree.add('newdir/newfile', 'file-id')
629
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
627
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
630
628
        differ.diff_text('file-id', None, 'old label', 'new label')
631
629
        self.assertEqual(
632
630
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
661
659
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
662
660
 
663
661
    def test_diff_symlink(self):
664
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
662
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
665
663
        differ.diff_symlink('old target', None)
666
664
        self.assertEqual("=== target was 'old target'\n",
667
665
                         differ.to_file.getvalue())
668
666
 
669
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
667
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
670
668
        differ.diff_symlink(None, 'new target')
671
669
        self.assertEqual("=== target is 'new target'\n",
672
670
                         differ.to_file.getvalue())
673
671
 
674
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
672
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
675
673
        differ.diff_symlink('old target', 'new target')
676
674
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
677
675
                         differ.to_file.getvalue())
692
690
             ' \@\@\n-old\n\+new\n\n')
693
691
 
694
692
    def test_diff_kind_change(self):
695
 
        self.requireFeature(tests.SymlinkFeature)
 
693
        self.requireFeature(features.SymlinkFeature)
696
694
        self.build_tree_contents([('old-tree/olddir/',),
697
695
                                  ('old-tree/olddir/oldfile', 'old\n')])
698
696
        self.old_tree.add('olddir')
727
725
 
728
726
    def test_register_diff(self):
729
727
        self.create_old_new()
730
 
        old_diff_factories = DiffTree.diff_factories
731
 
        DiffTree.diff_factories=old_diff_factories[:]
732
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
728
        old_diff_factories = diff.DiffTree.diff_factories
 
729
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
730
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
733
731
        try:
734
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
732
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
735
733
        finally:
736
 
            DiffTree.diff_factories = old_diff_factories
 
734
            diff.DiffTree.diff_factories = old_diff_factories
737
735
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
738
736
        self.assertNotContainsRe(
739
737
            differ.to_file.getvalue(),
744
742
 
745
743
    def test_extra_factories(self):
746
744
        self.create_old_new()
747
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
748
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
745
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
746
                               extra_factories=[DiffWasIs.from_diff_tree])
749
747
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
750
748
        self.assertNotContainsRe(
751
749
            differ.to_file.getvalue(),
764
762
            '.*a-file(.|\n)*b-file')
765
763
 
766
764
 
767
 
class TestPatienceDiffLib(TestCase):
 
765
class TestPatienceDiffLib(tests.TestCase):
768
766
 
769
767
    def setUp(self):
770
768
        super(TestPatienceDiffLib, self).setUp()
771
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
772
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
 
769
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
770
        self._recurse_matches = _patiencediff_py.recurse_matches_py
773
771
        self._PatienceSequenceMatcher = \
774
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
772
            _patiencediff_py.PatienceSequenceMatcher_py
775
773
 
776
774
    def test_diff_unicode_string(self):
777
775
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
1084
1082
                 'how are you today?\n']
1085
1083
        txt_b = ['hello there\n',
1086
1084
                 'how are you today?\n']
1087
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1085
        unified_diff = patiencediff.unified_diff
1088
1086
        psm = self._PatienceSequenceMatcher
1089
1087
        self.assertEquals(['--- \n',
1090
1088
                           '+++ \n',
1138
1136
                 'how are you today?\n']
1139
1137
        txt_b = ['hello there\n',
1140
1138
                 'how are you today?\n']
1141
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1139
        unified_diff = patiencediff.unified_diff
1142
1140
        psm = self._PatienceSequenceMatcher
1143
1141
        self.assertEquals(['--- a\t2008-08-08\n',
1144
1142
                           '+++ b\t2008-09-09\n',
1156
1154
 
1157
1155
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1158
1156
 
1159
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1157
    _test_needs_features = [features.compiled_patiencediff_feature]
1160
1158
 
1161
1159
    def setUp(self):
1162
1160
        super(TestPatienceDiffLib_c, self).setUp()
1163
 
        import bzrlib._patiencediff_c
1164
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1165
 
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
 
1161
        from bzrlib import _patiencediff_c
 
1162
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1163
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1166
1164
        self._PatienceSequenceMatcher = \
1167
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1165
            _patiencediff_c.PatienceSequenceMatcher_c
1168
1166
 
1169
1167
    def test_unhashable(self):
1170
1168
        """We should get a proper exception here."""
1180
1178
                                         None, ['valid'], ['valid', []])
1181
1179
 
1182
1180
 
1183
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1181
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1184
1182
 
1185
1183
    def setUp(self):
1186
1184
        super(TestPatienceDiffLibFiles, self).setUp()
1187
1185
        self._PatienceSequenceMatcher = \
1188
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1186
            _patiencediff_py.PatienceSequenceMatcher_py
1189
1187
 
1190
1188
    def test_patience_unified_diff_files(self):
1191
1189
        txt_a = ['hello there\n',
1196
1194
        open('a1', 'wb').writelines(txt_a)
1197
1195
        open('b1', 'wb').writelines(txt_b)
1198
1196
 
1199
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1197
        unified_diff_files = patiencediff.unified_diff_files
1200
1198
        psm = self._PatienceSequenceMatcher
1201
1199
        self.assertEquals(['--- a1\n',
1202
1200
                           '+++ b1\n',
1252
1250
 
1253
1251
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1254
1252
 
1255
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1253
    _test_needs_features = [features.compiled_patiencediff_feature]
1256
1254
 
1257
1255
    def setUp(self):
1258
1256
        super(TestPatienceDiffLibFiles_c, self).setUp()
1259
 
        import bzrlib._patiencediff_c
 
1257
        from bzrlib import _patiencediff_c
1260
1258
        self._PatienceSequenceMatcher = \
1261
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1262
 
 
1263
 
 
1264
 
class TestUsingCompiledIfAvailable(TestCase):
 
1259
            _patiencediff_c.PatienceSequenceMatcher_c
 
1260
 
 
1261
 
 
1262
class TestUsingCompiledIfAvailable(tests.TestCase):
1265
1263
 
1266
1264
    def test_PatienceSequenceMatcher(self):
1267
 
        if CompiledPatienceDiffFeature.available():
 
1265
        if features.compiled_patiencediff_feature.available():
1268
1266
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1269
1267
            self.assertIs(PatienceSequenceMatcher_c,
1270
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1268
                          patiencediff.PatienceSequenceMatcher)
1271
1269
        else:
1272
1270
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1273
1271
            self.assertIs(PatienceSequenceMatcher_py,
1274
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1272
                          patiencediff.PatienceSequenceMatcher)
1275
1273
 
1276
1274
    def test_unique_lcs(self):
1277
 
        if CompiledPatienceDiffFeature.available():
 
1275
        if features.compiled_patiencediff_feature.available():
1278
1276
            from bzrlib._patiencediff_c import unique_lcs_c
1279
1277
            self.assertIs(unique_lcs_c,
1280
 
                          bzrlib.patiencediff.unique_lcs)
 
1278
                          patiencediff.unique_lcs)
1281
1279
        else:
1282
1280
            from bzrlib._patiencediff_py import unique_lcs_py
1283
1281
            self.assertIs(unique_lcs_py,
1284
 
                          bzrlib.patiencediff.unique_lcs)
 
1282
                          patiencediff.unique_lcs)
1285
1283
 
1286
1284
    def test_recurse_matches(self):
1287
 
        if CompiledPatienceDiffFeature.available():
 
1285
        if features.compiled_patiencediff_feature.available():
1288
1286
            from bzrlib._patiencediff_c import recurse_matches_c
1289
1287
            self.assertIs(recurse_matches_c,
1290
 
                          bzrlib.patiencediff.recurse_matches)
 
1288
                          patiencediff.recurse_matches)
1291
1289
        else:
1292
1290
            from bzrlib._patiencediff_py import recurse_matches_py
1293
1291
            self.assertIs(recurse_matches_py,
1294
 
                          bzrlib.patiencediff.recurse_matches)
1295
 
 
1296
 
 
1297
 
class TestDiffFromTool(TestCaseWithTransport):
 
1292
                          patiencediff.recurse_matches)
 
1293
 
 
1294
 
 
1295
class TestDiffFromTool(tests.TestCaseWithTransport):
1298
1296
 
1299
1297
    def test_from_string(self):
1300
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1298
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1301
1299
        self.addCleanup(diff_obj.finish)
1302
1300
        self.assertEqual(['diff', '@old_path', '@new_path'],
1303
1301
            diff_obj.command_template)
1304
1302
 
1305
1303
    def test_from_string_u5(self):
1306
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
 
1304
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1305
                                                 None, None, None)
1307
1306
        self.addCleanup(diff_obj.finish)
1308
1307
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1309
1308
                         diff_obj.command_template)
1310
1309
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1311
1310
                         diff_obj._get_command('old-path', 'new-path'))
1312
1311
 
 
1312
    def test_from_string_path_with_backslashes(self):
 
1313
        self.requireFeature(features.backslashdir_feature)
 
1314
        tool = 'C:\\Tools\\Diff.exe'
 
1315
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
 
1316
        self.addCleanup(diff_obj.finish)
 
1317
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
1318
                         diff_obj.command_template)
 
1319
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
 
1320
                         diff_obj._get_command('old-path', 'new-path'))
 
1321
 
1313
1322
    def test_execute(self):
1314
1323
        output = StringIO()
1315
 
        diff_obj = DiffFromTool(['python', '-c',
1316
 
                                 'print "@old_path @new_path"'],
1317
 
                                None, None, output)
 
1324
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1325
                                      'print "@old_path @new_path"'],
 
1326
                                     None, None, output)
1318
1327
        self.addCleanup(diff_obj.finish)
1319
1328
        diff_obj._execute('old', 'new')
1320
1329
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1321
1330
 
1322
1331
    def test_excute_missing(self):
1323
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1324
 
                                None, None, None)
 
1332
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1333
                                     None, None, None)
1325
1334
        self.addCleanup(diff_obj.finish)
1326
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1327
 
                              'new')
 
1335
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1336
                              'old', 'new')
1328
1337
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1329
1338
                         ' on this machine', str(e))
1330
1339
 
1331
1340
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1332
 
        self.requireFeature(AttribFeature)
 
1341
        self.requireFeature(features.AttribFeature)
1333
1342
        output = StringIO()
1334
1343
        tree = self.make_branch_and_tree('tree')
1335
1344
        self.build_tree_contents([('tree/file', 'content')])
1337
1346
        tree.commit('old tree')
1338
1347
        tree.lock_read()
1339
1348
        self.addCleanup(tree.unlock)
1340
 
        diff_obj = DiffFromTool(['python', '-c',
1341
 
                                 'print "@old_path @new_path"'],
1342
 
                                tree, tree, output)
 
1349
        basis_tree = tree.basis_tree()
 
1350
        basis_tree.lock_read()
 
1351
        self.addCleanup(basis_tree.unlock)
 
1352
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1353
                                      'print "@old_path @new_path"'],
 
1354
                                     basis_tree, tree, output)
1343
1355
        diff_obj._prepare_files('file-id', 'file', 'file')
1344
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1345
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
 
1356
        # The old content should be readonly
 
1357
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
 
1358
                                    r'R.*old\\file$')
 
1359
        # The new content should use the tree object, not a 'new' file anymore
 
1360
        self.assertEndsWith(tree.basedir, 'work/tree')
 
1361
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1346
1362
 
1347
1363
    def assertReadableByAttrib(self, cwd, relpath, regex):
1348
1364
        proc = subprocess.Popen(['attrib', relpath],
1349
1365
                                stdout=subprocess.PIPE,
1350
1366
                                cwd=cwd)
1351
 
        proc.wait()
1352
 
        result = proc.stdout.read()
1353
 
        self.assertContainsRe(result, regex)
 
1367
        (result, err) = proc.communicate()
 
1368
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1354
1369
 
1355
1370
    def test_prepare_files(self):
1356
1371
        output = StringIO()
1359
1374
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1360
1375
        tree.add('oldname', 'file-id')
1361
1376
        tree.add('oldname2', 'file2-id')
1362
 
        tree.commit('old tree', timestamp=0)
 
1377
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1378
        tree.commit('old tree', timestamp=315532800)
1363
1379
        tree.rename_one('oldname', 'newname')
1364
1380
        tree.rename_one('oldname2', 'newname2')
1365
1381
        self.build_tree_contents([('tree/newname', 'newcontent')])
1369
1385
        self.addCleanup(old_tree.unlock)
1370
1386
        tree.lock_read()
1371
1387
        self.addCleanup(tree.unlock)
1372
 
        diff_obj = DiffFromTool(['python', '-c',
1373
 
                                 'print "@old_path @new_path"'],
1374
 
                                old_tree, tree, output)
 
1388
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1389
                                      'print "@old_path @new_path"'],
 
1390
                                     old_tree, tree, output)
1375
1391
        self.addCleanup(diff_obj.finish)
1376
1392
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1377
1393
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1378
1394
                                                     'newname')
1379
1395
        self.assertContainsRe(old_path, 'old/oldname$')
1380
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1381
 
        self.assertContainsRe(new_path, 'new/newname$')
 
1396
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
 
1397
        self.assertContainsRe(new_path, 'tree/newname$')
1382
1398
        self.assertFileEqual('oldcontent', old_path)
1383
1399
        self.assertFileEqual('newcontent', new_path)
1384
1400
        if osutils.host_os_dereferences_symlinks():
1387
1403
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1388
1404
 
1389
1405
 
1390
 
class TestGetTreesAndBranchesToDiff(TestCaseWithTransport):
 
1406
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1407
 
 
1408
    def test_encodable_filename(self):
 
1409
        # Just checks file path for external diff tool.
 
1410
        # We cannot change CPython's internal encoding used by os.exec*.
 
1411
        import sys
 
1412
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1413
                                    None, None, None)
 
1414
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1415
            encoding = scenario['encoding']
 
1416
            dirname  = scenario['info']['directory']
 
1417
            filename = scenario['info']['filename']
 
1418
 
 
1419
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1420
            relpath = dirname + u'/' + filename
 
1421
            fullpath = diffobj._safe_filename('safe', relpath)
 
1422
            self.assertEqual(
 
1423
                    fullpath,
 
1424
                    fullpath.encode(encoding).decode(encoding)
 
1425
                    )
 
1426
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1427
 
 
1428
    def test_unencodable_filename(self):
 
1429
        import sys
 
1430
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1431
                                    None, None, None)
 
1432
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1433
            encoding = scenario['encoding']
 
1434
            dirname  = scenario['info']['directory']
 
1435
            filename = scenario['info']['filename']
 
1436
 
 
1437
            if encoding == 'iso-8859-1':
 
1438
                encoding = 'iso-8859-2'
 
1439
            else:
 
1440
                encoding = 'iso-8859-1'
 
1441
 
 
1442
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1443
            relpath = dirname + u'/' + filename
 
1444
            fullpath = diffobj._safe_filename('safe', relpath)
 
1445
            self.assertEqual(
 
1446
                    fullpath,
 
1447
                    fullpath.encode(encoding).decode(encoding)
 
1448
                    )
 
1449
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1450
 
 
1451
 
 
1452
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1453
 
 
1454
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1455
        """Call get_trees_and_branches_to_diff_locked."""
 
1456
        return diff.get_trees_and_branches_to_diff_locked(
 
1457
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1391
1458
 
1392
1459
    def test_basic(self):
1393
1460
        tree = self.make_branch_and_tree('tree')
1394
1461
        (old_tree, new_tree,
1395
1462
         old_branch, new_branch,
1396
 
         specific_files, extra_trees) = \
1397
 
            get_trees_and_branches_to_diff(['tree'], None, None, None)
 
1463
         specific_files, extra_trees) = self.call_gtabtd(
 
1464
             ['tree'], None, None, None)
1398
1465
 
1399
 
        self.assertIsInstance(old_tree, RevisionTree)
1400
 
        #print dir (old_tree)
1401
 
        self.assertEqual(_mod_revision.NULL_REVISION, old_tree.get_revision_id())
 
1466
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1467
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1468
                         old_tree.get_revision_id())
1402
1469
        self.assertEqual(tree.basedir, new_tree.basedir)
1403
1470
        self.assertEqual(tree.branch.base, old_branch.base)
1404
1471
        self.assertEqual(tree.branch.base, new_branch.base)
1413
1480
        self.build_tree_contents([('tree/file', 'newcontent')])
1414
1481
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1415
1482
 
1416
 
        revisions = [RevisionSpec.from_string('1'),
1417
 
                     RevisionSpec.from_string('2')]
 
1483
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1484
                     revisionspec.RevisionSpec.from_string('2')]
1418
1485
        (old_tree, new_tree,
1419
1486
         old_branch, new_branch,
1420
 
         specific_files, extra_trees) = \
1421
 
            get_trees_and_branches_to_diff(['tree'], revisions, None, None)
 
1487
         specific_files, extra_trees) = self.call_gtabtd(
 
1488
            ['tree'], revisions, None, None)
1422
1489
 
1423
 
        self.assertIsInstance(old_tree, RevisionTree)
 
1490
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1424
1491
        self.assertEqual("old-id", old_tree.get_revision_id())
1425
 
        self.assertIsInstance(new_tree, RevisionTree)
 
1492
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1426
1493
        self.assertEqual("new-id", new_tree.get_revision_id())
1427
1494
        self.assertEqual(tree.branch.base, old_branch.base)
1428
1495
        self.assertEqual(tree.branch.base, new_branch.base)