~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: INADA Naoki
  • Date: 2011-05-05 09:15:34 UTC
  • mto: (5830.3.3 i18n-msgfmt)
  • mto: This revision was merged to the branch mainline in revision 5873.
  • Revision ID: songofacandy@gmail.com-20110505091534-7sv835xpofwrmpt4
Add update-pot command to Makefile and tools/bzrgettext script that
extracts help text from bzr commands.

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
 
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,
35
 
    get_trees_and_branches_to_diff,
 
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,
36
34
    )
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):
 
35
from bzrlib.symbol_versioning import deprecated_in
 
36
from bzrlib.tests import features
 
37
from bzrlib.tests.blackbox.test_diff import subst_dates
 
38
 
 
39
 
 
40
class _AttribFeature(tests.Feature):
50
41
 
51
42
    def _probe(self):
52
43
        if (sys.platform not in ('cygwin', 'win32')):
69
60
 
70
61
def udiff_lines(old, new, allow_binary=False):
71
62
    output = StringIO()
72
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
63
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
73
64
    output.seek(0, 0)
74
65
    return output.readlines()
75
66
 
79
70
        # StringIO has no fileno, so it tests a different codepath
80
71
        output = StringIO()
81
72
    else:
82
 
        output = TemporaryFile()
 
73
        output = tempfile.TemporaryFile()
83
74
    try:
84
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
85
 
    except NoDiff:
86
 
        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')
87
78
    output.seek(0, 0)
88
79
    lines = output.readlines()
89
80
    output.close()
90
81
    return lines
91
82
 
92
83
 
93
 
class TestDiff(TestCase):
 
84
class TestDiff(tests.TestCase):
94
85
 
95
86
    def test_add_nl(self):
96
87
        """diff generates a valid diff for patches that add a newline"""
132
123
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
133
124
 
134
125
    def test_binary_lines(self):
135
 
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
136
 
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
137
 
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
138
 
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
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)
139
132
 
140
133
    def test_external_diff(self):
141
134
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
151
144
        self.check_patch(lines)
152
145
 
153
146
    def test_external_diff_binary_lang_c(self):
154
 
        old_env = {}
155
147
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
156
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
157
 
        try:
158
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
159
 
            # Older versions of diffutils say "Binary files", newer
160
 
            # versions just say "Files".
161
 
            self.assertContainsRe(lines[0],
162
 
                                  '(Binary f|F)iles old and new differ\n')
163
 
            self.assertEquals(lines[1:], ['\n'])
164
 
        finally:
165
 
            for lang, old_val in old_env.iteritems():
166
 
                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'])
167
154
 
168
155
    def test_no_external_diff(self):
169
156
        """Check that NoDiff is raised when diff is not available"""
170
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
171
 
        orig_path = os.environ['PATH']
172
 
        try:
173
 
            os.environ['PATH'] = ''
174
 
            self.assertRaises(NoDiff, external_diff,
175
 
                              'old', ['boo\n'], 'new', ['goo\n'],
176
 
                              StringIO(), diff_opts=['-u'])
177
 
        finally:
178
 
            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'])
179
163
 
180
164
    def test_internal_diff_default(self):
181
165
        # Default internal diff encoding is utf8
182
166
        output = StringIO()
183
 
        internal_diff(u'old_\xb5', ['old_text\n'],
184
 
                    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)
185
169
        lines = output.getvalue().splitlines(True)
186
170
        self.check_patch(lines)
187
171
        self.assertEquals(['--- old_\xc2\xb5\n',
195
179
 
196
180
    def test_internal_diff_utf8(self):
197
181
        output = StringIO()
198
 
        internal_diff(u'old_\xb5', ['old_text\n'],
199
 
                    u'new_\xe5', ['new_text\n'], output,
200
 
                    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')
201
185
        lines = output.getvalue().splitlines(True)
202
186
        self.check_patch(lines)
203
187
        self.assertEquals(['--- old_\xc2\xb5\n',
211
195
 
212
196
    def test_internal_diff_iso_8859_1(self):
213
197
        output = StringIO()
214
 
        internal_diff(u'old_\xb5', ['old_text\n'],
215
 
                    u'new_\xe5', ['new_text\n'], output,
216
 
                    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')
217
201
        lines = output.getvalue().splitlines(True)
218
202
        self.check_patch(lines)
219
203
        self.assertEquals(['--- old_\xb5\n',
227
211
 
228
212
    def test_internal_diff_no_content(self):
229
213
        output = StringIO()
230
 
        internal_diff(u'old', [], u'new', [], output)
 
214
        diff.internal_diff(u'old', [], u'new', [], output)
231
215
        self.assertEqual('', output.getvalue())
232
216
 
233
217
    def test_internal_diff_no_changes(self):
234
218
        output = StringIO()
235
 
        internal_diff(u'old', ['text\n', 'contents\n'],
236
 
                      u'new', ['text\n', 'contents\n'],
237
 
                      output)
 
219
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
220
                           u'new', ['text\n', 'contents\n'],
 
221
                           output)
238
222
        self.assertEqual('', output.getvalue())
239
223
 
240
224
    def test_internal_diff_returns_bytes(self):
241
225
        import StringIO
242
226
        output = StringIO.StringIO()
243
 
        internal_diff(u'old_\xb5', ['old_text\n'],
244
 
                    u'new_\xe5', ['new_text\n'], output)
245
 
        self.failUnless(isinstance(output.getvalue(), str),
 
227
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
228
                            u'new_\xe5', ['new_text\n'], output)
 
229
        self.assertIsInstance(output.getvalue(), str,
246
230
            'internal_diff should return bytestrings')
247
231
 
248
232
 
249
 
class TestDiffFiles(TestCaseInTempDir):
 
233
class TestDiffFiles(tests.TestCaseInTempDir):
250
234
 
251
235
    def test_external_diff_binary(self):
252
236
        """The output when using external diff should use diff's i18n error"""
265
249
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
266
250
 
267
251
 
268
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
269
 
    """Has a helper for running show_diff_trees"""
270
 
 
271
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
272
 
        output = StringIO()
273
 
        if working_tree is not None:
274
 
            extra_trees = (working_tree,)
275
 
        else:
276
 
            extra_trees = ()
277
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
278
 
                        extra_trees=extra_trees, old_label='old/',
279
 
                        new_label='new/')
280
 
        return output.getvalue()
281
 
 
282
 
 
283
 
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):
284
266
 
285
267
    def setUp(self):
286
268
        super(TestDiffDates, self).setUp()
321
303
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
322
304
 
323
305
    def test_diff_rev_tree_working_tree(self):
324
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
306
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
325
307
        # note that the date for old/file1 is from rev 2 rather than from
326
308
        # the basis revision (rev 4)
327
309
        self.assertEqualDiff(output, '''\
337
319
    def test_diff_rev_tree_rev_tree(self):
338
320
        tree1 = self.b.repository.revision_tree('rev-2')
339
321
        tree2 = self.b.repository.revision_tree('rev-3')
340
 
        output = self.get_diff(tree1, tree2)
 
322
        output = get_diff_as_string(tree1, tree2)
341
323
        self.assertEqualDiff(output, '''\
342
324
=== modified file 'file2'
343
325
--- old/file2\t2006-04-01 00:00:00 +0000
351
333
    def test_diff_add_files(self):
352
334
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
353
335
        tree2 = self.b.repository.revision_tree('rev-1')
354
 
        output = self.get_diff(tree1, tree2)
 
336
        output = get_diff_as_string(tree1, tree2)
355
337
        # the files have the epoch time stamp for the tree in which
356
338
        # they don't exist.
357
339
        self.assertEqualDiff(output, '''\
372
354
    def test_diff_remove_files(self):
373
355
        tree1 = self.b.repository.revision_tree('rev-3')
374
356
        tree2 = self.b.repository.revision_tree('rev-4')
375
 
        output = self.get_diff(tree1, tree2)
 
357
        output = get_diff_as_string(tree1, tree2)
376
358
        # the file has the epoch time stamp for the tree in which
377
359
        # it doesn't exist.
378
360
        self.assertEqualDiff(output, '''\
389
371
        self.wt.rename_one('file1', 'file1b')
390
372
        old_tree = self.b.repository.revision_tree('rev-1')
391
373
        new_tree = self.b.repository.revision_tree('rev-4')
392
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
374
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
393
375
                            working_tree=self.wt)
394
376
        self.assertContainsRe(out, 'file1\t')
395
377
 
401
383
        self.wt.rename_one('file1', 'dir1/file1')
402
384
        old_tree = self.b.repository.revision_tree('rev-1')
403
385
        new_tree = self.b.repository.revision_tree('rev-4')
404
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
386
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
405
387
                            working_tree=self.wt)
406
388
        self.assertContainsRe(out, 'file1\t')
407
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
389
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
408
390
                            working_tree=self.wt)
409
391
        self.assertNotContainsRe(out, 'file1\t')
410
392
 
411
393
 
412
 
 
413
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
394
class TestShowDiffTrees(tests.TestCaseWithTransport):
414
395
    """Direct tests for show_diff_trees"""
415
396
 
416
397
    def test_modified_file(self):
421
402
        tree.commit('one', rev_id='rev-1')
422
403
 
423
404
        self.build_tree_contents([('tree/file', 'new contents\n')])
424
 
        diff = self.get_diff(tree.basis_tree(), tree)
425
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
426
 
        self.assertContainsRe(diff, '--- old/file\t')
427
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
428
 
        self.assertContainsRe(diff, '-contents\n'
429
 
                                    '\\+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')
430
411
 
431
412
    def test_modified_file_in_renamed_dir(self):
432
413
        """Test when a file is modified in a renamed directory."""
438
419
 
439
420
        tree.rename_one('dir', 'other')
440
421
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
441
 
        diff = self.get_diff(tree.basis_tree(), tree)
442
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
443
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
 
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")
444
425
        # XXX: This is technically incorrect, because it used to be at another
445
426
        # location. What to do?
446
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
447
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
448
 
        self.assertContainsRe(diff, '-contents\n'
449
 
                                    '\\+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')
450
431
 
451
432
    def test_renamed_directory(self):
452
433
        """Test when only a directory is only renamed."""
457
438
        tree.commit('one', rev_id='rev-1')
458
439
 
459
440
        tree.rename_one('dir', 'newdir')
460
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
441
        d = get_diff_as_string(tree.basis_tree(), tree)
461
442
        # Renaming a directory should be a single "you renamed this dir" even
462
443
        # when there are files inside.
463
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
444
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
464
445
 
465
446
    def test_renamed_file(self):
466
447
        """Test when a file is only renamed."""
470
451
        tree.commit('one', rev_id='rev-1')
471
452
 
472
453
        tree.rename_one('file', 'newname')
473
 
        diff = self.get_diff(tree.basis_tree(), tree)
474
 
        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")
475
456
        # We shouldn't have a --- or +++ line, because there is no content
476
457
        # change
477
 
        self.assertNotContainsRe(diff, '---')
 
458
        self.assertNotContainsRe(d, '---')
478
459
 
479
460
    def test_renamed_and_modified_file(self):
480
461
        """Test when a file is only renamed."""
485
466
 
486
467
        tree.rename_one('file', 'newname')
487
468
        self.build_tree_contents([('tree/newname', 'new contents\n')])
488
 
        diff = self.get_diff(tree.basis_tree(), tree)
489
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
490
 
        self.assertContainsRe(diff, '--- old/file\t')
491
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
492
 
        self.assertContainsRe(diff, '-contents\n'
493
 
                                    '\\+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')
494
475
 
495
476
 
496
477
    def test_internal_diff_exec_property(self):
515
496
        tree.rename_one('c', 'new-c')
516
497
        tree.rename_one('d', 'new-d')
517
498
 
518
 
        diff = self.get_diff(tree.basis_tree(), tree)
519
 
 
520
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
521
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
522
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
523
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
524
 
        self.assertNotContainsRe(diff, r"file 'e'")
525
 
        self.assertNotContainsRe(diff, r"file 'f'")
526
 
 
 
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'")
527
511
 
528
512
    def test_binary_unicode_filenames(self):
529
513
        """Test that contents of files are *not* encoded in UTF-8 when there
544
528
        tree.add([alpha], ['file-id'])
545
529
        tree.add([omega], ['file-id-2'])
546
530
        diff_content = StringIO()
547
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
548
 
        diff = diff_content.getvalue()
549
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
550
 
        self.assertContainsRe(
551
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
552
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
553
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
554
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
 
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,))
555
539
 
556
540
    def test_unicode_filename(self):
557
541
        """Test when the filename are unicode."""
576
560
        tree.add(['add_'+alpha], ['file-id'])
577
561
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
578
562
 
579
 
        diff = self.get_diff(tree.basis_tree(), tree)
580
 
        self.assertContainsRe(diff,
 
563
        d = get_diff_as_string(tree.basis_tree(), tree)
 
564
        self.assertContainsRe(d,
581
565
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
582
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
583
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
584
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
585
 
 
586
 
 
587
 
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):
588
615
 
589
616
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
590
617
        self.to_file.write('was: ')
594
621
        pass
595
622
 
596
623
 
597
 
class TestDiffTree(TestCaseWithTransport):
 
624
class TestDiffTree(tests.TestCaseWithTransport):
598
625
 
599
626
    def setUp(self):
600
 
        TestCaseWithTransport.setUp(self)
 
627
        super(TestDiffTree, self).setUp()
601
628
        self.old_tree = self.make_branch_and_tree('old-tree')
602
629
        self.old_tree.lock_write()
603
630
        self.addCleanup(self.old_tree.unlock)
604
631
        self.new_tree = self.make_branch_and_tree('new-tree')
605
632
        self.new_tree.lock_write()
606
633
        self.addCleanup(self.new_tree.unlock)
607
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
634
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
608
635
 
609
636
    def test_diff_text(self):
610
637
        self.build_tree_contents([('old-tree/olddir/',),
615
642
                                  ('new-tree/newdir/newfile', 'new\n')])
616
643
        self.new_tree.add('newdir')
617
644
        self.new_tree.add('newdir/newfile', 'file-id')
618
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
645
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
619
646
        differ.diff_text('file-id', None, 'old label', 'new label')
620
647
        self.assertEqual(
621
648
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
650
677
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
651
678
 
652
679
    def test_diff_symlink(self):
653
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
680
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
654
681
        differ.diff_symlink('old target', None)
655
682
        self.assertEqual("=== target was 'old target'\n",
656
683
                         differ.to_file.getvalue())
657
684
 
658
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
685
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
659
686
        differ.diff_symlink(None, 'new target')
660
687
        self.assertEqual("=== target is 'new target'\n",
661
688
                         differ.to_file.getvalue())
662
689
 
663
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
690
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
664
691
        differ.diff_symlink('old target', 'new target')
665
692
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
666
693
                         differ.to_file.getvalue())
716
743
 
717
744
    def test_register_diff(self):
718
745
        self.create_old_new()
719
 
        old_diff_factories = DiffTree.diff_factories
720
 
        DiffTree.diff_factories=old_diff_factories[:]
721
 
        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)
722
749
        try:
723
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
750
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
724
751
        finally:
725
 
            DiffTree.diff_factories = old_diff_factories
 
752
            diff.DiffTree.diff_factories = old_diff_factories
726
753
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
727
754
        self.assertNotContainsRe(
728
755
            differ.to_file.getvalue(),
733
760
 
734
761
    def test_extra_factories(self):
735
762
        self.create_old_new()
736
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
737
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
763
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
764
                               extra_factories=[DiffWasIs.from_diff_tree])
738
765
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
739
766
        self.assertNotContainsRe(
740
767
            differ.to_file.getvalue(),
753
780
            '.*a-file(.|\n)*b-file')
754
781
 
755
782
 
756
 
class TestPatienceDiffLib(TestCase):
 
783
class TestPatienceDiffLib(tests.TestCase):
757
784
 
758
785
    def setUp(self):
759
786
        super(TestPatienceDiffLib, self).setUp()
760
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
761
 
        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
762
789
        self._PatienceSequenceMatcher = \
763
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
790
            _patiencediff_py.PatienceSequenceMatcher_py
764
791
 
765
792
    def test_diff_unicode_string(self):
766
793
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
1073
1100
                 'how are you today?\n']
1074
1101
        txt_b = ['hello there\n',
1075
1102
                 'how are you today?\n']
1076
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1103
        unified_diff = patiencediff.unified_diff
1077
1104
        psm = self._PatienceSequenceMatcher
1078
1105
        self.assertEquals(['--- \n',
1079
1106
                           '+++ \n',
1127
1154
                 'how are you today?\n']
1128
1155
        txt_b = ['hello there\n',
1129
1156
                 'how are you today?\n']
1130
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1157
        unified_diff = patiencediff.unified_diff
1131
1158
        psm = self._PatienceSequenceMatcher
1132
1159
        self.assertEquals(['--- a\t2008-08-08\n',
1133
1160
                           '+++ b\t2008-09-09\n',
1149
1176
 
1150
1177
    def setUp(self):
1151
1178
        super(TestPatienceDiffLib_c, self).setUp()
1152
 
        import bzrlib._patiencediff_c
1153
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1154
 
        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
1155
1182
        self._PatienceSequenceMatcher = \
1156
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1183
            _patiencediff_c.PatienceSequenceMatcher_c
1157
1184
 
1158
1185
    def test_unhashable(self):
1159
1186
        """We should get a proper exception here."""
1169
1196
                                         None, ['valid'], ['valid', []])
1170
1197
 
1171
1198
 
1172
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1199
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1173
1200
 
1174
1201
    def setUp(self):
1175
1202
        super(TestPatienceDiffLibFiles, self).setUp()
1176
1203
        self._PatienceSequenceMatcher = \
1177
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1204
            _patiencediff_py.PatienceSequenceMatcher_py
1178
1205
 
1179
1206
    def test_patience_unified_diff_files(self):
1180
1207
        txt_a = ['hello there\n',
1185
1212
        open('a1', 'wb').writelines(txt_a)
1186
1213
        open('b1', 'wb').writelines(txt_b)
1187
1214
 
1188
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1215
        unified_diff_files = patiencediff.unified_diff_files
1189
1216
        psm = self._PatienceSequenceMatcher
1190
1217
        self.assertEquals(['--- a1\n',
1191
1218
                           '+++ b1\n',
1245
1272
 
1246
1273
    def setUp(self):
1247
1274
        super(TestPatienceDiffLibFiles_c, self).setUp()
1248
 
        import bzrlib._patiencediff_c
 
1275
        from bzrlib import _patiencediff_c
1249
1276
        self._PatienceSequenceMatcher = \
1250
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1251
 
 
1252
 
 
1253
 
class TestUsingCompiledIfAvailable(TestCase):
 
1277
            _patiencediff_c.PatienceSequenceMatcher_c
 
1278
 
 
1279
 
 
1280
class TestUsingCompiledIfAvailable(tests.TestCase):
1254
1281
 
1255
1282
    def test_PatienceSequenceMatcher(self):
1256
1283
        if compiled_patiencediff_feature.available():
1257
1284
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1258
1285
            self.assertIs(PatienceSequenceMatcher_c,
1259
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1286
                          patiencediff.PatienceSequenceMatcher)
1260
1287
        else:
1261
1288
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1262
1289
            self.assertIs(PatienceSequenceMatcher_py,
1263
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1290
                          patiencediff.PatienceSequenceMatcher)
1264
1291
 
1265
1292
    def test_unique_lcs(self):
1266
1293
        if compiled_patiencediff_feature.available():
1267
1294
            from bzrlib._patiencediff_c import unique_lcs_c
1268
1295
            self.assertIs(unique_lcs_c,
1269
 
                          bzrlib.patiencediff.unique_lcs)
 
1296
                          patiencediff.unique_lcs)
1270
1297
        else:
1271
1298
            from bzrlib._patiencediff_py import unique_lcs_py
1272
1299
            self.assertIs(unique_lcs_py,
1273
 
                          bzrlib.patiencediff.unique_lcs)
 
1300
                          patiencediff.unique_lcs)
1274
1301
 
1275
1302
    def test_recurse_matches(self):
1276
1303
        if compiled_patiencediff_feature.available():
1277
1304
            from bzrlib._patiencediff_c import recurse_matches_c
1278
1305
            self.assertIs(recurse_matches_c,
1279
 
                          bzrlib.patiencediff.recurse_matches)
 
1306
                          patiencediff.recurse_matches)
1280
1307
        else:
1281
1308
            from bzrlib._patiencediff_py import recurse_matches_py
1282
1309
            self.assertIs(recurse_matches_py,
1283
 
                          bzrlib.patiencediff.recurse_matches)
1284
 
 
1285
 
 
1286
 
class TestDiffFromTool(TestCaseWithTransport):
 
1310
                          patiencediff.recurse_matches)
 
1311
 
 
1312
 
 
1313
class TestDiffFromTool(tests.TestCaseWithTransport):
1287
1314
 
1288
1315
    def test_from_string(self):
1289
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1316
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1290
1317
        self.addCleanup(diff_obj.finish)
1291
1318
        self.assertEqual(['diff', '@old_path', '@new_path'],
1292
1319
            diff_obj.command_template)
1293
1320
 
1294
1321
    def test_from_string_u5(self):
1295
 
        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)
1296
1324
        self.addCleanup(diff_obj.finish)
1297
1325
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1298
1326
                         diff_obj.command_template)
1299
1327
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1300
1328
                         diff_obj._get_command('old-path', 'new-path'))
1301
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
 
1302
1340
    def test_execute(self):
1303
1341
        output = StringIO()
1304
 
        diff_obj = DiffFromTool(['python', '-c',
1305
 
                                 'print "@old_path @new_path"'],
1306
 
                                None, None, output)
 
1342
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1343
                                      'print "@old_path @new_path"'],
 
1344
                                     None, None, output)
1307
1345
        self.addCleanup(diff_obj.finish)
1308
1346
        diff_obj._execute('old', 'new')
1309
1347
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1310
1348
 
1311
1349
    def test_excute_missing(self):
1312
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1313
 
                                None, None, None)
 
1350
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1351
                                     None, None, None)
1314
1352
        self.addCleanup(diff_obj.finish)
1315
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1316
 
                              'new')
 
1353
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1354
                              'old', 'new')
1317
1355
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1318
1356
                         ' on this machine', str(e))
1319
1357
 
1329
1367
        basis_tree = tree.basis_tree()
1330
1368
        basis_tree.lock_read()
1331
1369
        self.addCleanup(basis_tree.unlock)
1332
 
        diff_obj = DiffFromTool(['python', '-c',
1333
 
                                 'print "@old_path @new_path"'],
1334
 
                                basis_tree, tree, output)
 
1370
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1371
                                      'print "@old_path @new_path"'],
 
1372
                                     basis_tree, tree, output)
1335
1373
        diff_obj._prepare_files('file-id', 'file', 'file')
1336
1374
        # The old content should be readonly
1337
1375
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1354
1392
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1355
1393
        tree.add('oldname', 'file-id')
1356
1394
        tree.add('oldname2', 'file2-id')
1357
 
        tree.commit('old tree', timestamp=0)
 
1395
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1396
        tree.commit('old tree', timestamp=315532800)
1358
1397
        tree.rename_one('oldname', 'newname')
1359
1398
        tree.rename_one('oldname2', 'newname2')
1360
1399
        self.build_tree_contents([('tree/newname', 'newcontent')])
1364
1403
        self.addCleanup(old_tree.unlock)
1365
1404
        tree.lock_read()
1366
1405
        self.addCleanup(tree.unlock)
1367
 
        diff_obj = DiffFromTool(['python', '-c',
1368
 
                                 'print "@old_path @new_path"'],
1369
 
                                old_tree, tree, output)
 
1406
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1407
                                      'print "@old_path @new_path"'],
 
1408
                                     old_tree, tree, output)
1370
1409
        self.addCleanup(diff_obj.finish)
1371
1410
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1372
1411
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1373
1412
                                                     'newname')
1374
1413
        self.assertContainsRe(old_path, 'old/oldname$')
1375
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
 
1414
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1376
1415
        self.assertContainsRe(new_path, 'tree/newname$')
1377
1416
        self.assertFileEqual('oldcontent', old_path)
1378
1417
        self.assertFileEqual('newcontent', new_path)
1382
1421
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1383
1422
 
1384
1423
 
1385
 
class TestGetTreesAndBranchesToDiff(TestCaseWithTransport):
 
1424
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1425
 
 
1426
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1427
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
 
1428
        TestGetTreesAndBranchesToDiff.
 
1429
        """
 
1430
        return diff.get_trees_and_branches_to_diff_locked(
 
1431
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1386
1432
 
1387
1433
    def test_basic(self):
1388
1434
        tree = self.make_branch_and_tree('tree')
1389
1435
        (old_tree, new_tree,
1390
1436
         old_branch, new_branch,
1391
 
         specific_files, extra_trees) = \
1392
 
            get_trees_and_branches_to_diff(['tree'], None, None, None)
 
1437
         specific_files, extra_trees) = self.call_gtabtd(
 
1438
             ['tree'], None, None, None)
1393
1439
 
1394
 
        self.assertIsInstance(old_tree, RevisionTree)
1395
 
        #print dir (old_tree)
1396
 
        self.assertEqual(_mod_revision.NULL_REVISION, old_tree.get_revision_id())
 
1440
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1441
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1442
                         old_tree.get_revision_id())
1397
1443
        self.assertEqual(tree.basedir, new_tree.basedir)
1398
1444
        self.assertEqual(tree.branch.base, old_branch.base)
1399
1445
        self.assertEqual(tree.branch.base, new_branch.base)
1408
1454
        self.build_tree_contents([('tree/file', 'newcontent')])
1409
1455
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1410
1456
 
1411
 
        revisions = [RevisionSpec.from_string('1'),
1412
 
                     RevisionSpec.from_string('2')]
 
1457
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1458
                     revisionspec.RevisionSpec.from_string('2')]
1413
1459
        (old_tree, new_tree,
1414
1460
         old_branch, new_branch,
1415
 
         specific_files, extra_trees) = \
1416
 
            get_trees_and_branches_to_diff(['tree'], revisions, None, None)
 
1461
         specific_files, extra_trees) = self.call_gtabtd(
 
1462
            ['tree'], revisions, None, None)
1417
1463
 
1418
 
        self.assertIsInstance(old_tree, RevisionTree)
 
1464
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1419
1465
        self.assertEqual("old-id", old_tree.get_revision_id())
1420
 
        self.assertIsInstance(new_tree, RevisionTree)
 
1466
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1421
1467
        self.assertEqual("new-id", new_tree.get_revision_id())
1422
1468
        self.assertEqual(tree.branch.base, old_branch.base)
1423
1469
        self.assertEqual(tree.branch.base, new_branch.base)
1424
1470
        self.assertIs(None, specific_files)
1425
1471
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
 
1472
 
 
1473
 
 
1474
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
 
1475
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
 
1476
    deprecated get_trees_and_branches_to_diff function.
 
1477
    """
 
1478
 
 
1479
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1480
        return self.applyDeprecated(
 
1481
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
 
1482
            path_list, revision_specs, old_url, new_url)