~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

(vila) Open 2.4.3 for bug fixes (Vincent Ladeuil)

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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
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
 
21
import tempfile
24
22
 
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,
 
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,
35
34
    )
36
 
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
37
 
import bzrlib.osutils as osutils
38
 
import bzrlib.revision as _mod_revision
39
 
import bzrlib.transform as transform
40
 
import bzrlib.patiencediff
41
 
import bzrlib._patiencediff_py
42
 
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
43
 
                          TestCaseInTempDir, TestSkipped)
44
 
 
45
 
 
46
 
class _AttribFeature(Feature):
 
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
 
 
39
 
 
40
class _AttribFeature(tests.Feature):
47
41
 
48
42
    def _probe(self):
49
43
        if (sys.platform not in ('cygwin', 'win32')):
60
54
AttribFeature = _AttribFeature()
61
55
 
62
56
 
63
 
class _CompiledPatienceDiffFeature(Feature):
64
 
 
65
 
    def _probe(self):
66
 
        try:
67
 
            import bzrlib._patiencediff_c
68
 
        except ImportError:
69
 
            return False
70
 
        return True
71
 
 
72
 
    def feature_name(self):
73
 
        return 'bzrlib._patiencediff_c'
74
 
 
75
 
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
57
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
 
58
                                    'bzrlib._patiencediff_c')
76
59
 
77
60
 
78
61
def udiff_lines(old, new, allow_binary=False):
79
62
    output = StringIO()
80
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
63
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
81
64
    output.seek(0, 0)
82
65
    return output.readlines()
83
66
 
87
70
        # StringIO has no fileno, so it tests a different codepath
88
71
        output = StringIO()
89
72
    else:
90
 
        output = TemporaryFile()
 
73
        output = tempfile.TemporaryFile()
91
74
    try:
92
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
93
 
    except NoDiff:
94
 
        raise TestSkipped('external "diff" not present to test')
 
75
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
76
    except errors.NoDiff:
 
77
        raise tests.TestSkipped('external "diff" not present to test')
95
78
    output.seek(0, 0)
96
79
    lines = output.readlines()
97
80
    output.close()
98
81
    return lines
99
82
 
100
83
 
101
 
class TestDiff(TestCase):
 
84
class TestDiff(tests.TestCase):
102
85
 
103
86
    def test_add_nl(self):
104
87
        """diff generates a valid diff for patches that add a newline"""
140
123
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
141
124
 
142
125
    def test_binary_lines(self):
143
 
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
144
 
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
145
 
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
146
 
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
126
        empty = []
 
127
        uni_lines = [1023 * 'a' + '\x00']
 
128
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
 
129
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
 
130
        udiff_lines(uni_lines , empty, allow_binary=True)
 
131
        udiff_lines(empty, uni_lines, allow_binary=True)
147
132
 
148
133
    def test_external_diff(self):
149
134
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
159
144
        self.check_patch(lines)
160
145
 
161
146
    def test_external_diff_binary_lang_c(self):
162
 
        old_env = {}
163
147
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
164
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
165
 
        try:
166
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
167
 
            # Older versions of diffutils say "Binary files", newer
168
 
            # versions just say "Files".
169
 
            self.assertContainsRe(lines[0],
170
 
                                  '(Binary f|F)iles old and new differ\n')
171
 
            self.assertEquals(lines[1:], ['\n'])
172
 
        finally:
173
 
            for lang, old_val in old_env.iteritems():
174
 
                osutils.set_or_unset_env(lang, old_val)
 
148
            self.overrideEnv(lang, 'C')
 
149
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
150
        # Older versions of diffutils say "Binary files", newer
 
151
        # versions just say "Files".
 
152
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
 
153
        self.assertEquals(lines[1:], ['\n'])
175
154
 
176
155
    def test_no_external_diff(self):
177
156
        """Check that NoDiff is raised when diff is not available"""
178
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
179
 
        orig_path = os.environ['PATH']
180
 
        try:
181
 
            os.environ['PATH'] = ''
182
 
            self.assertRaises(NoDiff, external_diff,
183
 
                              'old', ['boo\n'], 'new', ['goo\n'],
184
 
                              StringIO(), diff_opts=['-u'])
185
 
        finally:
186
 
            os.environ['PATH'] = orig_path
 
157
        # Make sure no 'diff' command is available
 
158
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
 
159
        self.overrideEnv('PATH', '')
 
160
        self.assertRaises(errors.NoDiff, diff.external_diff,
 
161
                          'old', ['boo\n'], 'new', ['goo\n'],
 
162
                          StringIO(), diff_opts=['-u'])
187
163
 
188
164
    def test_internal_diff_default(self):
189
165
        # Default internal diff encoding is utf8
190
166
        output = StringIO()
191
 
        internal_diff(u'old_\xb5', ['old_text\n'],
192
 
                    u'new_\xe5', ['new_text\n'], output)
 
167
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
168
                           u'new_\xe5', ['new_text\n'], output)
193
169
        lines = output.getvalue().splitlines(True)
194
170
        self.check_patch(lines)
195
171
        self.assertEquals(['--- old_\xc2\xb5\n',
203
179
 
204
180
    def test_internal_diff_utf8(self):
205
181
        output = StringIO()
206
 
        internal_diff(u'old_\xb5', ['old_text\n'],
207
 
                    u'new_\xe5', ['new_text\n'], output,
208
 
                    path_encoding='utf8')
 
182
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
183
                           u'new_\xe5', ['new_text\n'], output,
 
184
                           path_encoding='utf8')
209
185
        lines = output.getvalue().splitlines(True)
210
186
        self.check_patch(lines)
211
187
        self.assertEquals(['--- old_\xc2\xb5\n',
219
195
 
220
196
    def test_internal_diff_iso_8859_1(self):
221
197
        output = StringIO()
222
 
        internal_diff(u'old_\xb5', ['old_text\n'],
223
 
                    u'new_\xe5', ['new_text\n'], output,
224
 
                    path_encoding='iso-8859-1')
 
198
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
199
                           u'new_\xe5', ['new_text\n'], output,
 
200
                           path_encoding='iso-8859-1')
225
201
        lines = output.getvalue().splitlines(True)
226
202
        self.check_patch(lines)
227
203
        self.assertEquals(['--- old_\xb5\n',
235
211
 
236
212
    def test_internal_diff_no_content(self):
237
213
        output = StringIO()
238
 
        internal_diff(u'old', [], u'new', [], output)
 
214
        diff.internal_diff(u'old', [], u'new', [], output)
239
215
        self.assertEqual('', output.getvalue())
240
216
 
241
217
    def test_internal_diff_no_changes(self):
242
218
        output = StringIO()
243
 
        internal_diff(u'old', ['text\n', 'contents\n'],
244
 
                      u'new', ['text\n', 'contents\n'],
245
 
                      output)
 
219
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
220
                           u'new', ['text\n', 'contents\n'],
 
221
                           output)
246
222
        self.assertEqual('', output.getvalue())
247
223
 
248
224
    def test_internal_diff_returns_bytes(self):
249
225
        import StringIO
250
226
        output = StringIO.StringIO()
251
 
        internal_diff(u'old_\xb5', ['old_text\n'],
252
 
                    u'new_\xe5', ['new_text\n'], output)
253
 
        self.failUnless(isinstance(output.getvalue(), str),
 
227
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
228
                            u'new_\xe5', ['new_text\n'], output)
 
229
        self.assertIsInstance(output.getvalue(), str,
254
230
            'internal_diff should return bytestrings')
255
231
 
256
232
 
257
 
class TestDiffFiles(TestCaseInTempDir):
 
233
class TestDiffFiles(tests.TestCaseInTempDir):
258
234
 
259
235
    def test_external_diff_binary(self):
260
236
        """The output when using external diff should use diff's i18n error"""
273
249
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
274
250
 
275
251
 
276
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
277
 
    """Has a helper for running show_diff_trees"""
278
 
 
279
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
280
 
        output = StringIO()
281
 
        if working_tree is not None:
282
 
            extra_trees = (working_tree,)
283
 
        else:
284
 
            extra_trees = ()
285
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
286
 
                        extra_trees=extra_trees, old_label='old/',
287
 
                        new_label='new/')
288
 
        return output.getvalue()
289
 
 
290
 
 
291
 
class TestDiffDates(TestShowDiffTreesHelper):
 
252
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
253
    output = StringIO()
 
254
    if working_tree is not None:
 
255
        extra_trees = (working_tree,)
 
256
    else:
 
257
        extra_trees = ()
 
258
    diff.show_diff_trees(tree1, tree2, output,
 
259
        specific_files=specific_files,
 
260
        extra_trees=extra_trees, old_label='old/',
 
261
        new_label='new/')
 
262
    return output.getvalue()
 
263
 
 
264
 
 
265
class TestDiffDates(tests.TestCaseWithTransport):
292
266
 
293
267
    def setUp(self):
294
268
        super(TestDiffDates, self).setUp()
329
303
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
330
304
 
331
305
    def test_diff_rev_tree_working_tree(self):
332
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
306
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
333
307
        # note that the date for old/file1 is from rev 2 rather than from
334
308
        # the basis revision (rev 4)
335
309
        self.assertEqualDiff(output, '''\
345
319
    def test_diff_rev_tree_rev_tree(self):
346
320
        tree1 = self.b.repository.revision_tree('rev-2')
347
321
        tree2 = self.b.repository.revision_tree('rev-3')
348
 
        output = self.get_diff(tree1, tree2)
 
322
        output = get_diff_as_string(tree1, tree2)
349
323
        self.assertEqualDiff(output, '''\
350
324
=== modified file 'file2'
351
325
--- old/file2\t2006-04-01 00:00:00 +0000
359
333
    def test_diff_add_files(self):
360
334
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
361
335
        tree2 = self.b.repository.revision_tree('rev-1')
362
 
        output = self.get_diff(tree1, tree2)
 
336
        output = get_diff_as_string(tree1, tree2)
363
337
        # the files have the epoch time stamp for the tree in which
364
338
        # they don't exist.
365
339
        self.assertEqualDiff(output, '''\
380
354
    def test_diff_remove_files(self):
381
355
        tree1 = self.b.repository.revision_tree('rev-3')
382
356
        tree2 = self.b.repository.revision_tree('rev-4')
383
 
        output = self.get_diff(tree1, tree2)
 
357
        output = get_diff_as_string(tree1, tree2)
384
358
        # the file has the epoch time stamp for the tree in which
385
359
        # it doesn't exist.
386
360
        self.assertEqualDiff(output, '''\
397
371
        self.wt.rename_one('file1', 'file1b')
398
372
        old_tree = self.b.repository.revision_tree('rev-1')
399
373
        new_tree = self.b.repository.revision_tree('rev-4')
400
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
374
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
401
375
                            working_tree=self.wt)
402
376
        self.assertContainsRe(out, 'file1\t')
403
377
 
409
383
        self.wt.rename_one('file1', 'dir1/file1')
410
384
        old_tree = self.b.repository.revision_tree('rev-1')
411
385
        new_tree = self.b.repository.revision_tree('rev-4')
412
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
386
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
413
387
                            working_tree=self.wt)
414
388
        self.assertContainsRe(out, 'file1\t')
415
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
389
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
416
390
                            working_tree=self.wt)
417
391
        self.assertNotContainsRe(out, 'file1\t')
418
392
 
419
393
 
420
 
 
421
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
394
class TestShowDiffTrees(tests.TestCaseWithTransport):
422
395
    """Direct tests for show_diff_trees"""
423
396
 
424
397
    def test_modified_file(self):
429
402
        tree.commit('one', rev_id='rev-1')
430
403
 
431
404
        self.build_tree_contents([('tree/file', 'new contents\n')])
432
 
        diff = self.get_diff(tree.basis_tree(), tree)
433
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
434
 
        self.assertContainsRe(diff, '--- old/file\t')
435
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
436
 
        self.assertContainsRe(diff, '-contents\n'
437
 
                                    '\\+new contents\n')
 
405
        d = get_diff_as_string(tree.basis_tree(), tree)
 
406
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
407
        self.assertContainsRe(d, '--- old/file\t')
 
408
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
409
        self.assertContainsRe(d, '-contents\n'
 
410
                                 '\\+new contents\n')
438
411
 
439
412
    def test_modified_file_in_renamed_dir(self):
440
413
        """Test when a file is modified in a renamed directory."""
446
419
 
447
420
        tree.rename_one('dir', 'other')
448
421
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
449
 
        diff = self.get_diff(tree.basis_tree(), tree)
450
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
451
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
 
422
        d = get_diff_as_string(tree.basis_tree(), tree)
 
423
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
424
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
452
425
        # XXX: This is technically incorrect, because it used to be at another
453
426
        # location. What to do?
454
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
455
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
456
 
        self.assertContainsRe(diff, '-contents\n'
457
 
                                    '\\+new contents\n')
 
427
        self.assertContainsRe(d, '--- old/dir/file\t')
 
428
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
429
        self.assertContainsRe(d, '-contents\n'
 
430
                                 '\\+new contents\n')
458
431
 
459
432
    def test_renamed_directory(self):
460
433
        """Test when only a directory is only renamed."""
465
438
        tree.commit('one', rev_id='rev-1')
466
439
 
467
440
        tree.rename_one('dir', 'newdir')
468
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
441
        d = get_diff_as_string(tree.basis_tree(), tree)
469
442
        # Renaming a directory should be a single "you renamed this dir" even
470
443
        # when there are files inside.
471
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
444
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
472
445
 
473
446
    def test_renamed_file(self):
474
447
        """Test when a file is only renamed."""
478
451
        tree.commit('one', rev_id='rev-1')
479
452
 
480
453
        tree.rename_one('file', 'newname')
481
 
        diff = self.get_diff(tree.basis_tree(), tree)
482
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
454
        d = get_diff_as_string(tree.basis_tree(), tree)
 
455
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
456
        # We shouldn't have a --- or +++ line, because there is no content
484
457
        # change
485
 
        self.assertNotContainsRe(diff, '---')
 
458
        self.assertNotContainsRe(d, '---')
486
459
 
487
460
    def test_renamed_and_modified_file(self):
488
461
        """Test when a file is only renamed."""
493
466
 
494
467
        tree.rename_one('file', 'newname')
495
468
        self.build_tree_contents([('tree/newname', 'new contents\n')])
496
 
        diff = self.get_diff(tree.basis_tree(), tree)
497
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
498
 
        self.assertContainsRe(diff, '--- old/file\t')
499
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
500
 
        self.assertContainsRe(diff, '-contents\n'
501
 
                                    '\\+new contents\n')
 
469
        d = get_diff_as_string(tree.basis_tree(), tree)
 
470
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
471
        self.assertContainsRe(d, '--- old/file\t')
 
472
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
473
        self.assertContainsRe(d, '-contents\n'
 
474
                                 '\\+new contents\n')
502
475
 
503
476
 
504
477
    def test_internal_diff_exec_property(self):
523
496
        tree.rename_one('c', 'new-c')
524
497
        tree.rename_one('d', 'new-d')
525
498
 
526
 
        diff = self.get_diff(tree.basis_tree(), tree)
527
 
 
528
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
529
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
530
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
531
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
532
 
        self.assertNotContainsRe(diff, r"file 'e'")
533
 
        self.assertNotContainsRe(diff, r"file 'f'")
534
 
 
 
499
        d = get_diff_as_string(tree.basis_tree(), tree)
 
500
 
 
501
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
502
                                  ".*\+x to -x.*\)")
 
503
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
504
                                  ".*-x to \+x.*\)")
 
505
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
506
                                  ".*\+x to -x.*\)")
 
507
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
508
                                  ".*-x to \+x.*\)")
 
509
        self.assertNotContainsRe(d, r"file 'e'")
 
510
        self.assertNotContainsRe(d, r"file 'f'")
535
511
 
536
512
    def test_binary_unicode_filenames(self):
537
513
        """Test that contents of files are *not* encoded in UTF-8 when there
552
528
        tree.add([alpha], ['file-id'])
553
529
        tree.add([omega], ['file-id-2'])
554
530
        diff_content = StringIO()
555
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
556
 
        diff = diff_content.getvalue()
557
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
558
 
        self.assertContainsRe(
559
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
560
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
561
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
562
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
 
531
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
 
532
        d = diff_content.getvalue()
 
533
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
 
534
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
535
                              % (alpha_utf8, alpha_utf8))
 
536
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
 
537
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
 
538
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
563
539
 
564
540
    def test_unicode_filename(self):
565
541
        """Test when the filename are unicode."""
584
560
        tree.add(['add_'+alpha], ['file-id'])
585
561
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
586
562
 
587
 
        diff = self.get_diff(tree.basis_tree(), tree)
588
 
        self.assertContainsRe(diff,
 
563
        d = get_diff_as_string(tree.basis_tree(), tree)
 
564
        self.assertContainsRe(d,
589
565
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
590
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
591
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
592
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
593
 
 
594
 
 
595
 
class DiffWasIs(DiffPath):
 
566
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
 
567
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
 
568
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
 
569
 
 
570
    def test_unicode_filename_path_encoding(self):
 
571
        """Test for bug #382699: unicode filenames on Windows should be shown
 
572
        in user encoding.
 
573
        """
 
574
        self.requireFeature(tests.UnicodeFilenameFeature)
 
575
        # The word 'test' in Russian
 
576
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
577
        directory = _russian_test + u'/'
 
578
        test_txt = _russian_test + u'.txt'
 
579
        u1234 = u'\u1234.txt'
 
580
 
 
581
        tree = self.make_branch_and_tree('.')
 
582
        self.build_tree_contents([
 
583
            (test_txt, 'foo\n'),
 
584
            (u1234, 'foo\n'),
 
585
            (directory, None),
 
586
            ])
 
587
        tree.add([test_txt, u1234, directory])
 
588
 
 
589
        sio = StringIO()
 
590
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
591
            path_encoding='cp1251')
 
592
 
 
593
        output = subst_dates(sio.getvalue())
 
594
        shouldbe = ('''\
 
595
=== added directory '%(directory)s'
 
596
=== added file '%(test_txt)s'
 
597
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
598
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
599
@@ -0,0 +1,1 @@
 
600
+foo
 
601
 
 
602
=== added file '?.txt'
 
603
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
604
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
605
@@ -0,0 +1,1 @@
 
606
+foo
 
607
 
 
608
''' % {'directory': _russian_test.encode('cp1251'),
 
609
       'test_txt': test_txt.encode('cp1251'),
 
610
      })
 
611
        self.assertEqualDiff(output, shouldbe)
 
612
 
 
613
 
 
614
class DiffWasIs(diff.DiffPath):
596
615
 
597
616
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
598
617
        self.to_file.write('was: ')
602
621
        pass
603
622
 
604
623
 
605
 
class TestDiffTree(TestCaseWithTransport):
 
624
class TestDiffTree(tests.TestCaseWithTransport):
606
625
 
607
626
    def setUp(self):
608
 
        TestCaseWithTransport.setUp(self)
 
627
        super(TestDiffTree, self).setUp()
609
628
        self.old_tree = self.make_branch_and_tree('old-tree')
610
629
        self.old_tree.lock_write()
611
630
        self.addCleanup(self.old_tree.unlock)
612
631
        self.new_tree = self.make_branch_and_tree('new-tree')
613
632
        self.new_tree.lock_write()
614
633
        self.addCleanup(self.new_tree.unlock)
615
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
634
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
616
635
 
617
636
    def test_diff_text(self):
618
637
        self.build_tree_contents([('old-tree/olddir/',),
623
642
                                  ('new-tree/newdir/newfile', 'new\n')])
624
643
        self.new_tree.add('newdir')
625
644
        self.new_tree.add('newdir/newfile', 'file-id')
626
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
645
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
627
646
        differ.diff_text('file-id', None, 'old label', 'new label')
628
647
        self.assertEqual(
629
648
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
658
677
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
659
678
 
660
679
    def test_diff_symlink(self):
661
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
680
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
662
681
        differ.diff_symlink('old target', None)
663
682
        self.assertEqual("=== target was 'old target'\n",
664
683
                         differ.to_file.getvalue())
665
684
 
666
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
685
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
667
686
        differ.diff_symlink(None, 'new target')
668
687
        self.assertEqual("=== target is 'new target'\n",
669
688
                         differ.to_file.getvalue())
670
689
 
671
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
690
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
672
691
        differ.diff_symlink('old target', 'new target')
673
692
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
674
693
                         differ.to_file.getvalue())
704
723
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
705
724
             ' \@\@\n-old\n\n')
706
725
        self.assertContainsRe(self.differ.to_file.getvalue(),
707
 
                              "=== target is 'new'\n")
 
726
                              "=== target is u'new'\n")
708
727
 
709
728
    def test_diff_directory(self):
710
729
        self.build_tree(['new-tree/new-dir/'])
724
743
 
725
744
    def test_register_diff(self):
726
745
        self.create_old_new()
727
 
        old_diff_factories = DiffTree.diff_factories
728
 
        DiffTree.diff_factories=old_diff_factories[:]
729
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
746
        old_diff_factories = diff.DiffTree.diff_factories
 
747
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
748
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
730
749
        try:
731
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
750
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
732
751
        finally:
733
 
            DiffTree.diff_factories = old_diff_factories
 
752
            diff.DiffTree.diff_factories = old_diff_factories
734
753
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
735
754
        self.assertNotContainsRe(
736
755
            differ.to_file.getvalue(),
741
760
 
742
761
    def test_extra_factories(self):
743
762
        self.create_old_new()
744
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
745
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
763
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
764
                               extra_factories=[DiffWasIs.from_diff_tree])
746
765
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
747
766
        self.assertNotContainsRe(
748
767
            differ.to_file.getvalue(),
761
780
            '.*a-file(.|\n)*b-file')
762
781
 
763
782
 
764
 
class TestPatienceDiffLib(TestCase):
 
783
class TestPatienceDiffLib(tests.TestCase):
765
784
 
766
785
    def setUp(self):
767
786
        super(TestPatienceDiffLib, self).setUp()
768
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
769
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
 
787
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
788
        self._recurse_matches = _patiencediff_py.recurse_matches_py
770
789
        self._PatienceSequenceMatcher = \
771
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
790
            _patiencediff_py.PatienceSequenceMatcher_py
772
791
 
773
792
    def test_diff_unicode_string(self):
774
793
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
1081
1100
                 'how are you today?\n']
1082
1101
        txt_b = ['hello there\n',
1083
1102
                 'how are you today?\n']
1084
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1103
        unified_diff = patiencediff.unified_diff
1085
1104
        psm = self._PatienceSequenceMatcher
1086
1105
        self.assertEquals(['--- \n',
1087
1106
                           '+++ \n',
1135
1154
                 'how are you today?\n']
1136
1155
        txt_b = ['hello there\n',
1137
1156
                 'how are you today?\n']
1138
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1157
        unified_diff = patiencediff.unified_diff
1139
1158
        psm = self._PatienceSequenceMatcher
1140
1159
        self.assertEquals(['--- a\t2008-08-08\n',
1141
1160
                           '+++ b\t2008-09-09\n',
1153
1172
 
1154
1173
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1155
1174
 
1156
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1175
    _test_needs_features = [compiled_patiencediff_feature]
1157
1176
 
1158
1177
    def setUp(self):
1159
1178
        super(TestPatienceDiffLib_c, self).setUp()
1160
 
        import bzrlib._patiencediff_c
1161
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1162
 
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
 
1179
        from bzrlib import _patiencediff_c
 
1180
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1181
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1163
1182
        self._PatienceSequenceMatcher = \
1164
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1183
            _patiencediff_c.PatienceSequenceMatcher_c
1165
1184
 
1166
1185
    def test_unhashable(self):
1167
1186
        """We should get a proper exception here."""
1177
1196
                                         None, ['valid'], ['valid', []])
1178
1197
 
1179
1198
 
1180
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1199
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1181
1200
 
1182
1201
    def setUp(self):
1183
1202
        super(TestPatienceDiffLibFiles, self).setUp()
1184
1203
        self._PatienceSequenceMatcher = \
1185
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1204
            _patiencediff_py.PatienceSequenceMatcher_py
1186
1205
 
1187
1206
    def test_patience_unified_diff_files(self):
1188
1207
        txt_a = ['hello there\n',
1193
1212
        open('a1', 'wb').writelines(txt_a)
1194
1213
        open('b1', 'wb').writelines(txt_b)
1195
1214
 
1196
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1215
        unified_diff_files = patiencediff.unified_diff_files
1197
1216
        psm = self._PatienceSequenceMatcher
1198
1217
        self.assertEquals(['--- a1\n',
1199
1218
                           '+++ b1\n',
1249
1268
 
1250
1269
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1251
1270
 
1252
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1271
    _test_needs_features = [compiled_patiencediff_feature]
1253
1272
 
1254
1273
    def setUp(self):
1255
1274
        super(TestPatienceDiffLibFiles_c, self).setUp()
1256
 
        import bzrlib._patiencediff_c
 
1275
        from bzrlib import _patiencediff_c
1257
1276
        self._PatienceSequenceMatcher = \
1258
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1259
 
 
1260
 
 
1261
 
class TestUsingCompiledIfAvailable(TestCase):
 
1277
            _patiencediff_c.PatienceSequenceMatcher_c
 
1278
 
 
1279
 
 
1280
class TestUsingCompiledIfAvailable(tests.TestCase):
1262
1281
 
1263
1282
    def test_PatienceSequenceMatcher(self):
1264
 
        if CompiledPatienceDiffFeature.available():
 
1283
        if compiled_patiencediff_feature.available():
1265
1284
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1266
1285
            self.assertIs(PatienceSequenceMatcher_c,
1267
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1286
                          patiencediff.PatienceSequenceMatcher)
1268
1287
        else:
1269
1288
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1270
1289
            self.assertIs(PatienceSequenceMatcher_py,
1271
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1290
                          patiencediff.PatienceSequenceMatcher)
1272
1291
 
1273
1292
    def test_unique_lcs(self):
1274
 
        if CompiledPatienceDiffFeature.available():
 
1293
        if compiled_patiencediff_feature.available():
1275
1294
            from bzrlib._patiencediff_c import unique_lcs_c
1276
1295
            self.assertIs(unique_lcs_c,
1277
 
                          bzrlib.patiencediff.unique_lcs)
 
1296
                          patiencediff.unique_lcs)
1278
1297
        else:
1279
1298
            from bzrlib._patiencediff_py import unique_lcs_py
1280
1299
            self.assertIs(unique_lcs_py,
1281
 
                          bzrlib.patiencediff.unique_lcs)
 
1300
                          patiencediff.unique_lcs)
1282
1301
 
1283
1302
    def test_recurse_matches(self):
1284
 
        if CompiledPatienceDiffFeature.available():
 
1303
        if compiled_patiencediff_feature.available():
1285
1304
            from bzrlib._patiencediff_c import recurse_matches_c
1286
1305
            self.assertIs(recurse_matches_c,
1287
 
                          bzrlib.patiencediff.recurse_matches)
 
1306
                          patiencediff.recurse_matches)
1288
1307
        else:
1289
1308
            from bzrlib._patiencediff_py import recurse_matches_py
1290
1309
            self.assertIs(recurse_matches_py,
1291
 
                          bzrlib.patiencediff.recurse_matches)
1292
 
 
1293
 
 
1294
 
class TestDiffFromTool(TestCaseWithTransport):
 
1310
                          patiencediff.recurse_matches)
 
1311
 
 
1312
 
 
1313
class TestDiffFromTool(tests.TestCaseWithTransport):
1295
1314
 
1296
1315
    def test_from_string(self):
1297
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1316
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1298
1317
        self.addCleanup(diff_obj.finish)
1299
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
 
1318
        self.assertEqual(['diff', '@old_path', '@new_path'],
1300
1319
            diff_obj.command_template)
1301
1320
 
1302
1321
    def test_from_string_u5(self):
1303
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
 
1322
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1323
                                                 None, None, None)
1304
1324
        self.addCleanup(diff_obj.finish)
1305
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
 
1325
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1306
1326
                         diff_obj.command_template)
1307
1327
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1308
1328
                         diff_obj._get_command('old-path', 'new-path'))
1309
1329
 
 
1330
    def test_from_string_path_with_backslashes(self):
 
1331
        self.requireFeature(features.backslashdir_feature)
 
1332
        tool = 'C:\\Tools\\Diff.exe'
 
1333
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
 
1334
        self.addCleanup(diff_obj.finish)
 
1335
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
1336
                         diff_obj.command_template)
 
1337
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
 
1338
                         diff_obj._get_command('old-path', 'new-path'))
 
1339
 
1310
1340
    def test_execute(self):
1311
1341
        output = StringIO()
1312
 
        diff_obj = DiffFromTool(['python', '-c',
1313
 
                                 'print "%(old_path)s %(new_path)s"'],
1314
 
                                None, None, output)
 
1342
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1343
                                      'print "@old_path @new_path"'],
 
1344
                                     None, None, output)
1315
1345
        self.addCleanup(diff_obj.finish)
1316
1346
        diff_obj._execute('old', 'new')
1317
1347
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1318
1348
 
1319
1349
    def test_excute_missing(self):
1320
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
 
                                None, None, None)
 
1350
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1351
                                     None, None, None)
1322
1352
        self.addCleanup(diff_obj.finish)
1323
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1324
 
                              'new')
 
1353
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1354
                              'old', 'new')
1325
1355
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1326
1356
                         ' on this machine', str(e))
1327
1357
 
1334
1364
        tree.commit('old tree')
1335
1365
        tree.lock_read()
1336
1366
        self.addCleanup(tree.unlock)
1337
 
        diff_obj = DiffFromTool(['python', '-c',
1338
 
                                 'print "%(old_path)s %(new_path)s"'],
1339
 
                                tree, tree, output)
 
1367
        basis_tree = tree.basis_tree()
 
1368
        basis_tree.lock_read()
 
1369
        self.addCleanup(basis_tree.unlock)
 
1370
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1371
                                      'print "@old_path @new_path"'],
 
1372
                                     basis_tree, tree, output)
1340
1373
        diff_obj._prepare_files('file-id', 'file', 'file')
1341
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1342
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
 
1374
        # The old content should be readonly
 
1375
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
 
1376
                                    r'R.*old\\file$')
 
1377
        # The new content should use the tree object, not a 'new' file anymore
 
1378
        self.assertEndsWith(tree.basedir, 'work/tree')
 
1379
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1343
1380
 
1344
1381
    def assertReadableByAttrib(self, cwd, relpath, regex):
1345
1382
        proc = subprocess.Popen(['attrib', relpath],
1346
1383
                                stdout=subprocess.PIPE,
1347
1384
                                cwd=cwd)
1348
 
        proc.wait()
1349
 
        result = proc.stdout.read()
1350
 
        self.assertContainsRe(result, regex)
 
1385
        (result, err) = proc.communicate()
 
1386
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1351
1387
 
1352
1388
    def test_prepare_files(self):
1353
1389
        output = StringIO()
1356
1392
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1357
1393
        tree.add('oldname', 'file-id')
1358
1394
        tree.add('oldname2', 'file2-id')
1359
 
        tree.commit('old tree', timestamp=0)
 
1395
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1396
        tree.commit('old tree', timestamp=315532800)
1360
1397
        tree.rename_one('oldname', 'newname')
1361
1398
        tree.rename_one('oldname2', 'newname2')
1362
1399
        self.build_tree_contents([('tree/newname', 'newcontent')])
1366
1403
        self.addCleanup(old_tree.unlock)
1367
1404
        tree.lock_read()
1368
1405
        self.addCleanup(tree.unlock)
1369
 
        diff_obj = DiffFromTool(['python', '-c',
1370
 
                                 'print "%(old_path)s %(new_path)s"'],
1371
 
                                old_tree, tree, output)
 
1406
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1407
                                      'print "@old_path @new_path"'],
 
1408
                                     old_tree, tree, output)
1372
1409
        self.addCleanup(diff_obj.finish)
1373
1410
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1374
1411
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1375
1412
                                                     'newname')
1376
1413
        self.assertContainsRe(old_path, 'old/oldname$')
1377
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1378
 
        self.assertContainsRe(new_path, 'new/newname$')
 
1414
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
 
1415
        self.assertContainsRe(new_path, 'tree/newname$')
1379
1416
        self.assertFileEqual('oldcontent', old_path)
1380
1417
        self.assertFileEqual('newcontent', new_path)
1381
1418
        if osutils.host_os_dereferences_symlinks():
1382
1419
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1383
1420
        # make sure we can create files with the same parent directories
1384
1421
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
 
1422
 
 
1423
 
 
1424
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1425
 
 
1426
    def test_encodable_filename(self):
 
1427
        # Just checks file path for external diff tool.
 
1428
        # We cannot change CPython's internal encoding used by os.exec*.
 
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
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1438
            relpath = dirname + u'/' + filename
 
1439
            fullpath = diffobj._safe_filename('safe', relpath)
 
1440
            self.assertEqual(
 
1441
                    fullpath,
 
1442
                    fullpath.encode(encoding).decode(encoding)
 
1443
                    )
 
1444
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1445
 
 
1446
    def test_unencodable_filename(self):
 
1447
        import sys
 
1448
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1449
                                    None, None, None)
 
1450
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1451
            encoding = scenario['encoding']
 
1452
            dirname  = scenario['info']['directory']
 
1453
            filename = scenario['info']['filename']
 
1454
 
 
1455
            if encoding == 'iso-8859-1':
 
1456
                encoding = 'iso-8859-2'
 
1457
            else:
 
1458
                encoding = 'iso-8859-1'
 
1459
 
 
1460
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1461
            relpath = dirname + u'/' + filename
 
1462
            fullpath = diffobj._safe_filename('safe', relpath)
 
1463
            self.assertEqual(
 
1464
                    fullpath,
 
1465
                    fullpath.encode(encoding).decode(encoding)
 
1466
                    )
 
1467
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1468
 
 
1469
 
 
1470
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1471
 
 
1472
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1473
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
 
1474
        TestGetTreesAndBranchesToDiff.
 
1475
        """
 
1476
        return diff.get_trees_and_branches_to_diff_locked(
 
1477
            path_list, revision_specs, old_url, new_url, self.addCleanup)
 
1478
 
 
1479
    def test_basic(self):
 
1480
        tree = self.make_branch_and_tree('tree')
 
1481
        (old_tree, new_tree,
 
1482
         old_branch, new_branch,
 
1483
         specific_files, extra_trees) = self.call_gtabtd(
 
1484
             ['tree'], None, None, None)
 
1485
 
 
1486
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1487
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1488
                         old_tree.get_revision_id())
 
1489
        self.assertEqual(tree.basedir, new_tree.basedir)
 
1490
        self.assertEqual(tree.branch.base, old_branch.base)
 
1491
        self.assertEqual(tree.branch.base, new_branch.base)
 
1492
        self.assertIs(None, specific_files)
 
1493
        self.assertIs(None, extra_trees)
 
1494
 
 
1495
    def test_with_rev_specs(self):
 
1496
        tree = self.make_branch_and_tree('tree')
 
1497
        self.build_tree_contents([('tree/file', 'oldcontent')])
 
1498
        tree.add('file', 'file-id')
 
1499
        tree.commit('old tree', timestamp=0, rev_id="old-id")
 
1500
        self.build_tree_contents([('tree/file', 'newcontent')])
 
1501
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1502
 
 
1503
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1504
                     revisionspec.RevisionSpec.from_string('2')]
 
1505
        (old_tree, new_tree,
 
1506
         old_branch, new_branch,
 
1507
         specific_files, extra_trees) = self.call_gtabtd(
 
1508
            ['tree'], revisions, None, None)
 
1509
 
 
1510
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1511
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1512
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
 
1513
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1514
        self.assertEqual(tree.branch.base, old_branch.base)
 
1515
        self.assertEqual(tree.branch.base, new_branch.base)
 
1516
        self.assertIs(None, specific_files)
 
1517
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
 
1518
 
 
1519
 
 
1520
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
 
1521
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
 
1522
    deprecated get_trees_and_branches_to_diff function.
 
1523
    """
 
1524
 
 
1525
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1526
        return self.applyDeprecated(
 
1527
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
 
1528
            path_list, revision_specs, old_url, new_url)