~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 16:58:03 UTC
  • mfrom: (3224.3.1 news-typo)
  • Revision ID: pqm@pqm.ubuntu.com-20080316165803-tisoc9mpob9z544o
(Matt Nordhoff) Trivial NEWS typo fix

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
 
18
import os.path
18
19
from cStringIO import StringIO
 
20
import errno
19
21
import subprocess
20
 
import sys
21
 
import tempfile
22
 
 
23
 
from bzrlib import (
24
 
    diff,
25
 
    errors,
26
 
    osutils,
27
 
    patiencediff,
28
 
    _patiencediff_py,
29
 
    revision as _mod_revision,
30
 
    revisionspec,
31
 
    revisiontree,
32
 
    tests,
33
 
    transform,
34
 
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import features, EncodingAdapter
37
 
from bzrlib.tests.blackbox.test_diff import subst_dates
38
 
from bzrlib.tests import (
39
 
    features,
40
 
    )
41
 
 
 
22
from tempfile import TemporaryFile
 
23
 
 
24
from bzrlib import tests
 
25
from bzrlib.diff import (
 
26
    DiffFromTool,
 
27
    DiffPath,
 
28
    DiffSymlink,
 
29
    DiffTree,
 
30
    DiffText,
 
31
    external_diff,
 
32
    internal_diff,
 
33
    show_diff_trees,
 
34
    )
 
35
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
 
36
import bzrlib.osutils as osutils
 
37
import bzrlib.patiencediff
 
38
import bzrlib._patiencediff_py
 
39
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
 
40
                          TestCaseInTempDir, TestSkipped)
 
41
 
 
42
 
 
43
class _CompiledPatienceDiffFeature(Feature):
 
44
 
 
45
    def _probe(self):
 
46
        try:
 
47
            import bzrlib._patiencediff_c
 
48
        except ImportError:
 
49
            return False
 
50
        return True
 
51
 
 
52
    def feature_name(self):
 
53
        return 'bzrlib._patiencediff_c'
 
54
 
 
55
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
56
 
 
57
 
 
58
class _UnicodeFilename(Feature):
 
59
    """Does the filesystem support Unicode filenames?"""
 
60
 
 
61
    def _probe(self):
 
62
        try:
 
63
            os.stat(u'\u03b1')
 
64
        except UnicodeEncodeError:
 
65
            return False
 
66
        except (IOError, OSError):
 
67
            # The filesystem allows the Unicode filename but the file doesn't
 
68
            # exist.
 
69
            return True
 
70
        else:
 
71
            # The filesystem allows the Unicode filename and the file exists,
 
72
            # for some reason.
 
73
            return True
 
74
 
 
75
UnicodeFilename = _UnicodeFilename()
 
76
 
 
77
 
 
78
class TestUnicodeFilename(TestCase):
 
79
 
 
80
    def test_probe_passes(self):
 
81
        """UnicodeFilename._probe passes."""
 
82
        # We can't test much more than that because the behaviour depends
 
83
        # on the platform.
 
84
        UnicodeFilename._probe()
 
85
        
42
86
 
43
87
def udiff_lines(old, new, allow_binary=False):
44
88
    output = StringIO()
45
 
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
 
89
    internal_diff('old', old, 'new', new, output, allow_binary)
46
90
    output.seek(0, 0)
47
91
    return output.readlines()
48
92
 
52
96
        # StringIO has no fileno, so it tests a different codepath
53
97
        output = StringIO()
54
98
    else:
55
 
        output = tempfile.TemporaryFile()
 
99
        output = TemporaryFile()
56
100
    try:
57
 
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
58
 
    except errors.NoDiff:
59
 
        raise tests.TestSkipped('external "diff" not present to test')
 
101
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
102
    except NoDiff:
 
103
        raise TestSkipped('external "diff" not present to test')
60
104
    output.seek(0, 0)
61
105
    lines = output.readlines()
62
106
    output.close()
63
107
    return lines
64
108
 
65
109
 
66
 
class TestDiff(tests.TestCase):
 
110
class TestDiff(TestCase):
67
111
 
68
112
    def test_add_nl(self):
69
113
        """diff generates a valid diff for patches that add a newline"""
105
149
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
106
150
 
107
151
    def test_binary_lines(self):
108
 
        empty = []
109
 
        uni_lines = [1023 * 'a' + '\x00']
110
 
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
111
 
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
112
 
        udiff_lines(uni_lines , empty, allow_binary=True)
113
 
        udiff_lines(empty, uni_lines, allow_binary=True)
 
152
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
 
153
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
 
154
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
 
155
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
114
156
 
115
157
    def test_external_diff(self):
116
158
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
126
168
        self.check_patch(lines)
127
169
 
128
170
    def test_external_diff_binary_lang_c(self):
 
171
        old_env = {}
129
172
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
130
 
            self.overrideEnv(lang, 'C')
131
 
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
132
 
        # Older versions of diffutils say "Binary files", newer
133
 
        # versions just say "Files".
134
 
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
135
 
        self.assertEquals(lines[1:], ['\n'])
 
173
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
 
174
        try:
 
175
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
176
            # Older versions of diffutils say "Binary files", newer
 
177
            # versions just say "Files".
 
178
            self.assertContainsRe(lines[0],
 
179
                                  '(Binary f|F)iles old and new differ\n')
 
180
            self.assertEquals(lines[1:], ['\n'])
 
181
        finally:
 
182
            for lang, old_val in old_env.iteritems():
 
183
                osutils.set_or_unset_env(lang, old_val)
136
184
 
137
185
    def test_no_external_diff(self):
138
186
        """Check that NoDiff is raised when diff is not available"""
139
 
        # Make sure no 'diff' command is available
140
 
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
141
 
        self.overrideEnv('PATH', '')
142
 
        self.assertRaises(errors.NoDiff, diff.external_diff,
143
 
                          'old', ['boo\n'], 'new', ['goo\n'],
144
 
                          StringIO(), diff_opts=['-u'])
145
 
 
 
187
        # Use os.environ['PATH'] to make sure no 'diff' command is available
 
188
        orig_path = os.environ['PATH']
 
189
        try:
 
190
            os.environ['PATH'] = ''
 
191
            self.assertRaises(NoDiff, external_diff,
 
192
                              'old', ['boo\n'], 'new', ['goo\n'],
 
193
                              StringIO(), diff_opts=['-u'])
 
194
        finally:
 
195
            os.environ['PATH'] = orig_path
 
196
        
146
197
    def test_internal_diff_default(self):
147
198
        # Default internal diff encoding is utf8
148
199
        output = StringIO()
149
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
150
 
                           u'new_\xe5', ['new_text\n'], output)
 
200
        internal_diff(u'old_\xb5', ['old_text\n'],
 
201
                    u'new_\xe5', ['new_text\n'], output)
151
202
        lines = output.getvalue().splitlines(True)
152
203
        self.check_patch(lines)
153
204
        self.assertEquals(['--- old_\xc2\xb5\n',
161
212
 
162
213
    def test_internal_diff_utf8(self):
163
214
        output = StringIO()
164
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
165
 
                           u'new_\xe5', ['new_text\n'], output,
166
 
                           path_encoding='utf8')
 
215
        internal_diff(u'old_\xb5', ['old_text\n'],
 
216
                    u'new_\xe5', ['new_text\n'], output,
 
217
                    path_encoding='utf8')
167
218
        lines = output.getvalue().splitlines(True)
168
219
        self.check_patch(lines)
169
220
        self.assertEquals(['--- old_\xc2\xb5\n',
177
228
 
178
229
    def test_internal_diff_iso_8859_1(self):
179
230
        output = StringIO()
180
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
181
 
                           u'new_\xe5', ['new_text\n'], output,
182
 
                           path_encoding='iso-8859-1')
 
231
        internal_diff(u'old_\xb5', ['old_text\n'],
 
232
                    u'new_\xe5', ['new_text\n'], output,
 
233
                    path_encoding='iso-8859-1')
183
234
        lines = output.getvalue().splitlines(True)
184
235
        self.check_patch(lines)
185
236
        self.assertEquals(['--- old_\xb5\n',
193
244
 
194
245
    def test_internal_diff_no_content(self):
195
246
        output = StringIO()
196
 
        diff.internal_diff(u'old', [], u'new', [], output)
 
247
        internal_diff(u'old', [], u'new', [], output)
197
248
        self.assertEqual('', output.getvalue())
198
249
 
199
250
    def test_internal_diff_no_changes(self):
200
251
        output = StringIO()
201
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
202
 
                           u'new', ['text\n', 'contents\n'],
203
 
                           output)
 
252
        internal_diff(u'old', ['text\n', 'contents\n'],
 
253
                      u'new', ['text\n', 'contents\n'],
 
254
                      output)
204
255
        self.assertEqual('', output.getvalue())
205
256
 
206
257
    def test_internal_diff_returns_bytes(self):
207
258
        import StringIO
208
259
        output = StringIO.StringIO()
209
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
210
 
                            u'new_\xe5', ['new_text\n'], output)
211
 
        self.assertIsInstance(output.getvalue(), str,
 
260
        internal_diff(u'old_\xb5', ['old_text\n'],
 
261
                    u'new_\xe5', ['new_text\n'], output)
 
262
        self.failUnless(isinstance(output.getvalue(), str),
212
263
            'internal_diff should return bytestrings')
213
264
 
214
265
 
215
 
class TestDiffFiles(tests.TestCaseInTempDir):
 
266
class TestDiffFiles(TestCaseInTempDir):
216
267
 
217
268
    def test_external_diff_binary(self):
218
269
        """The output when using external diff should use diff's i18n error"""
231
282
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
232
283
 
233
284
 
234
 
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
235
 
    output = StringIO()
236
 
    if working_tree is not None:
237
 
        extra_trees = (working_tree,)
238
 
    else:
239
 
        extra_trees = ()
240
 
    diff.show_diff_trees(tree1, tree2, output,
241
 
        specific_files=specific_files,
242
 
        extra_trees=extra_trees, old_label='old/',
243
 
        new_label='new/')
244
 
    return output.getvalue()
245
 
 
246
 
 
247
 
class TestDiffDates(tests.TestCaseWithTransport):
 
285
class TestShowDiffTreesHelper(TestCaseWithTransport):
 
286
    """Has a helper for running show_diff_trees"""
 
287
 
 
288
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
 
289
        output = StringIO()
 
290
        if working_tree is not None:
 
291
            extra_trees = (working_tree,)
 
292
        else:
 
293
            extra_trees = ()
 
294
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
 
295
                        extra_trees=extra_trees, old_label='old/',
 
296
                        new_label='new/')
 
297
        return output.getvalue()
 
298
 
 
299
 
 
300
class TestDiffDates(TestShowDiffTreesHelper):
248
301
 
249
302
    def setUp(self):
250
303
        super(TestDiffDates, self).setUp()
285
338
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
286
339
 
287
340
    def test_diff_rev_tree_working_tree(self):
288
 
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
 
341
        output = self.get_diff(self.wt.basis_tree(), self.wt)
289
342
        # note that the date for old/file1 is from rev 2 rather than from
290
343
        # the basis revision (rev 4)
291
344
        self.assertEqualDiff(output, '''\
301
354
    def test_diff_rev_tree_rev_tree(self):
302
355
        tree1 = self.b.repository.revision_tree('rev-2')
303
356
        tree2 = self.b.repository.revision_tree('rev-3')
304
 
        output = get_diff_as_string(tree1, tree2)
 
357
        output = self.get_diff(tree1, tree2)
305
358
        self.assertEqualDiff(output, '''\
306
359
=== modified file 'file2'
307
360
--- old/file2\t2006-04-01 00:00:00 +0000
311
364
+file2 contents at rev 3
312
365
 
313
366
''')
314
 
 
 
367
        
315
368
    def test_diff_add_files(self):
316
 
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
 
369
        tree1 = self.b.repository.revision_tree(None)
317
370
        tree2 = self.b.repository.revision_tree('rev-1')
318
 
        output = get_diff_as_string(tree1, tree2)
 
371
        output = self.get_diff(tree1, tree2)
319
372
        # the files have the epoch time stamp for the tree in which
320
373
        # they don't exist.
321
374
        self.assertEqualDiff(output, '''\
336
389
    def test_diff_remove_files(self):
337
390
        tree1 = self.b.repository.revision_tree('rev-3')
338
391
        tree2 = self.b.repository.revision_tree('rev-4')
339
 
        output = get_diff_as_string(tree1, tree2)
 
392
        output = self.get_diff(tree1, tree2)
340
393
        # the file has the epoch time stamp for the tree in which
341
394
        # it doesn't exist.
342
395
        self.assertEqualDiff(output, '''\
353
406
        self.wt.rename_one('file1', 'file1b')
354
407
        old_tree = self.b.repository.revision_tree('rev-1')
355
408
        new_tree = self.b.repository.revision_tree('rev-4')
356
 
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
 
409
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
357
410
                            working_tree=self.wt)
358
411
        self.assertContainsRe(out, 'file1\t')
359
412
 
365
418
        self.wt.rename_one('file1', 'dir1/file1')
366
419
        old_tree = self.b.repository.revision_tree('rev-1')
367
420
        new_tree = self.b.repository.revision_tree('rev-4')
368
 
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
 
421
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
369
422
                            working_tree=self.wt)
370
423
        self.assertContainsRe(out, 'file1\t')
371
 
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
 
424
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
372
425
                            working_tree=self.wt)
373
426
        self.assertNotContainsRe(out, 'file1\t')
374
427
 
375
428
 
376
 
class TestShowDiffTrees(tests.TestCaseWithTransport):
 
429
 
 
430
class TestShowDiffTrees(TestShowDiffTreesHelper):
377
431
    """Direct tests for show_diff_trees"""
378
432
 
379
433
    def test_modified_file(self):
384
438
        tree.commit('one', rev_id='rev-1')
385
439
 
386
440
        self.build_tree_contents([('tree/file', 'new contents\n')])
387
 
        d = get_diff_as_string(tree.basis_tree(), tree)
388
 
        self.assertContainsRe(d, "=== modified file 'file'\n")
389
 
        self.assertContainsRe(d, '--- old/file\t')
390
 
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
391
 
        self.assertContainsRe(d, '-contents\n'
392
 
                                 '\\+new contents\n')
 
441
        diff = self.get_diff(tree.basis_tree(), tree)
 
442
        self.assertContainsRe(diff, "=== modified file 'file'\n")
 
443
        self.assertContainsRe(diff, '--- old/file\t')
 
444
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
 
445
        self.assertContainsRe(diff, '-contents\n'
 
446
                                    '\\+new contents\n')
393
447
 
394
448
    def test_modified_file_in_renamed_dir(self):
395
449
        """Test when a file is modified in a renamed directory."""
401
455
 
402
456
        tree.rename_one('dir', 'other')
403
457
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
404
 
        d = get_diff_as_string(tree.basis_tree(), tree)
405
 
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
406
 
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
 
458
        diff = self.get_diff(tree.basis_tree(), tree)
 
459
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
 
460
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
407
461
        # XXX: This is technically incorrect, because it used to be at another
408
462
        # location. What to do?
409
 
        self.assertContainsRe(d, '--- old/dir/file\t')
410
 
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
411
 
        self.assertContainsRe(d, '-contents\n'
412
 
                                 '\\+new contents\n')
 
463
        self.assertContainsRe(diff, '--- old/dir/file\t')
 
464
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
 
465
        self.assertContainsRe(diff, '-contents\n'
 
466
                                    '\\+new contents\n')
413
467
 
414
468
    def test_renamed_directory(self):
415
469
        """Test when only a directory is only renamed."""
420
474
        tree.commit('one', rev_id='rev-1')
421
475
 
422
476
        tree.rename_one('dir', 'newdir')
423
 
        d = get_diff_as_string(tree.basis_tree(), tree)
 
477
        diff = self.get_diff(tree.basis_tree(), tree)
424
478
        # Renaming a directory should be a single "you renamed this dir" even
425
479
        # when there are files inside.
426
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
480
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
427
481
 
428
482
    def test_renamed_file(self):
429
483
        """Test when a file is only renamed."""
433
487
        tree.commit('one', rev_id='rev-1')
434
488
 
435
489
        tree.rename_one('file', 'newname')
436
 
        d = get_diff_as_string(tree.basis_tree(), tree)
437
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
490
        diff = self.get_diff(tree.basis_tree(), tree)
 
491
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
438
492
        # We shouldn't have a --- or +++ line, because there is no content
439
493
        # change
440
 
        self.assertNotContainsRe(d, '---')
 
494
        self.assertNotContainsRe(diff, '---')
441
495
 
442
496
    def test_renamed_and_modified_file(self):
443
497
        """Test when a file is only renamed."""
448
502
 
449
503
        tree.rename_one('file', 'newname')
450
504
        self.build_tree_contents([('tree/newname', 'new contents\n')])
451
 
        d = get_diff_as_string(tree.basis_tree(), tree)
452
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
453
 
        self.assertContainsRe(d, '--- old/file\t')
454
 
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
455
 
        self.assertContainsRe(d, '-contents\n'
456
 
                                 '\\+new contents\n')
457
 
 
458
 
 
459
 
    def test_internal_diff_exec_property(self):
460
 
        tree = self.make_branch_and_tree('tree')
461
 
 
462
 
        tt = transform.TreeTransform(tree)
463
 
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
464
 
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
465
 
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
466
 
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
467
 
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
468
 
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
469
 
        tt.apply()
470
 
        tree.commit('one', rev_id='rev-1')
471
 
 
472
 
        tt = transform.TreeTransform(tree)
473
 
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
474
 
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
475
 
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
476
 
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
477
 
        tt.apply()
478
 
        tree.rename_one('c', 'new-c')
479
 
        tree.rename_one('d', 'new-d')
480
 
 
481
 
        d = get_diff_as_string(tree.basis_tree(), tree)
482
 
 
483
 
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
484
 
                                  ".*\+x to -x.*\)")
485
 
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
486
 
                                  ".*-x to \+x.*\)")
487
 
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
488
 
                                  ".*\+x to -x.*\)")
489
 
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
490
 
                                  ".*-x to \+x.*\)")
491
 
        self.assertNotContainsRe(d, r"file 'e'")
492
 
        self.assertNotContainsRe(d, r"file 'f'")
 
505
        diff = self.get_diff(tree.basis_tree(), tree)
 
506
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
507
        self.assertContainsRe(diff, '--- old/file\t')
 
508
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
 
509
        self.assertContainsRe(diff, '-contents\n'
 
510
                                    '\\+new contents\n')
493
511
 
494
512
    def test_binary_unicode_filenames(self):
495
513
        """Test that contents of files are *not* encoded in UTF-8 when there
496
514
        is a binary file in the diff.
497
515
        """
498
516
        # See https://bugs.launchpad.net/bugs/110092.
499
 
        self.requireFeature(features.UnicodeFilenameFeature)
 
517
        self.requireFeature(UnicodeFilename)
500
518
 
501
519
        # This bug isn't triggered with cStringIO.
502
520
        from StringIO import StringIO
510
528
        tree.add([alpha], ['file-id'])
511
529
        tree.add([omega], ['file-id-2'])
512
530
        diff_content = StringIO()
513
 
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
514
 
        d = diff_content.getvalue()
515
 
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
516
 
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
517
 
                              % (alpha_utf8, alpha_utf8))
518
 
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
519
 
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
520
 
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
 
531
        show_diff_trees(tree.basis_tree(), tree, diff_content)
 
532
        diff = diff_content.getvalue()
 
533
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
 
534
        self.assertContainsRe(
 
535
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
 
536
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
 
537
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
 
538
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
521
539
 
522
540
    def test_unicode_filename(self):
523
541
        """Test when the filename are unicode."""
524
 
        self.requireFeature(features.UnicodeFilenameFeature)
 
542
        self.requireFeature(UnicodeFilename)
525
543
 
526
544
        alpha, omega = u'\u03b1', u'\u03c9'
527
545
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
542
560
        tree.add(['add_'+alpha], ['file-id'])
543
561
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
544
562
 
545
 
        d = get_diff_as_string(tree.basis_tree(), tree)
546
 
        self.assertContainsRe(d,
 
563
        diff = self.get_diff(tree.basis_tree(), tree)
 
564
        self.assertContainsRe(diff,
547
565
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
548
 
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
549
 
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
550
 
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
551
 
 
552
 
    def test_unicode_filename_path_encoding(self):
553
 
        """Test for bug #382699: unicode filenames on Windows should be shown
554
 
        in user encoding.
555
 
        """
556
 
        self.requireFeature(features.UnicodeFilenameFeature)
557
 
        # The word 'test' in Russian
558
 
        _russian_test = u'\u0422\u0435\u0441\u0442'
559
 
        directory = _russian_test + u'/'
560
 
        test_txt = _russian_test + u'.txt'
561
 
        u1234 = u'\u1234.txt'
562
 
 
563
 
        tree = self.make_branch_and_tree('.')
564
 
        self.build_tree_contents([
565
 
            (test_txt, 'foo\n'),
566
 
            (u1234, 'foo\n'),
567
 
            (directory, None),
568
 
            ])
569
 
        tree.add([test_txt, u1234, directory])
570
 
 
571
 
        sio = StringIO()
572
 
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
573
 
            path_encoding='cp1251')
574
 
 
575
 
        output = subst_dates(sio.getvalue())
576
 
        shouldbe = ('''\
577
 
=== added directory '%(directory)s'
578
 
=== added file '%(test_txt)s'
579
 
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
580
 
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
581
 
@@ -0,0 +1,1 @@
582
 
+foo
583
 
 
584
 
=== added file '?.txt'
585
 
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
586
 
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
587
 
@@ -0,0 +1,1 @@
588
 
+foo
589
 
 
590
 
''' % {'directory': _russian_test.encode('cp1251'),
591
 
       'test_txt': test_txt.encode('cp1251'),
592
 
      })
593
 
        self.assertEqualDiff(output, shouldbe)
594
 
 
595
 
 
596
 
class DiffWasIs(diff.DiffPath):
 
566
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
 
567
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
 
568
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
 
569
 
 
570
 
 
571
class DiffWasIs(DiffPath):
597
572
 
598
573
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
599
574
        self.to_file.write('was: ')
603
578
        pass
604
579
 
605
580
 
606
 
class TestDiffTree(tests.TestCaseWithTransport):
 
581
class TestDiffTree(TestCaseWithTransport):
607
582
 
608
583
    def setUp(self):
609
 
        super(TestDiffTree, self).setUp()
 
584
        TestCaseWithTransport.setUp(self)
610
585
        self.old_tree = self.make_branch_and_tree('old-tree')
611
586
        self.old_tree.lock_write()
612
587
        self.addCleanup(self.old_tree.unlock)
613
588
        self.new_tree = self.make_branch_and_tree('new-tree')
614
589
        self.new_tree.lock_write()
615
590
        self.addCleanup(self.new_tree.unlock)
616
 
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
591
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
617
592
 
618
593
    def test_diff_text(self):
619
594
        self.build_tree_contents([('old-tree/olddir/',),
624
599
                                  ('new-tree/newdir/newfile', 'new\n')])
625
600
        self.new_tree.add('newdir')
626
601
        self.new_tree.add('newdir/newfile', 'file-id')
627
 
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
 
602
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
628
603
        differ.diff_text('file-id', None, 'old label', 'new label')
629
604
        self.assertEqual(
630
605
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
659
634
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
660
635
 
661
636
    def test_diff_symlink(self):
662
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
637
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
663
638
        differ.diff_symlink('old target', None)
664
639
        self.assertEqual("=== target was 'old target'\n",
665
640
                         differ.to_file.getvalue())
666
641
 
667
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
642
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
668
643
        differ.diff_symlink(None, 'new target')
669
644
        self.assertEqual("=== target is 'new target'\n",
670
645
                         differ.to_file.getvalue())
671
646
 
672
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
647
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
673
648
        differ.diff_symlink('old target', 'new target')
674
649
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
675
650
                         differ.to_file.getvalue())
690
665
             ' \@\@\n-old\n\+new\n\n')
691
666
 
692
667
    def test_diff_kind_change(self):
693
 
        self.requireFeature(features.SymlinkFeature)
 
668
        self.requireFeature(tests.SymlinkFeature)
694
669
        self.build_tree_contents([('old-tree/olddir/',),
695
670
                                  ('old-tree/olddir/oldfile', 'old\n')])
696
671
        self.old_tree.add('olddir')
705
680
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
706
681
             ' \@\@\n-old\n\n')
707
682
        self.assertContainsRe(self.differ.to_file.getvalue(),
708
 
                              "=== target is u'new'\n")
 
683
                              "=== target is 'new'\n")
709
684
 
710
685
    def test_diff_directory(self):
711
686
        self.build_tree(['new-tree/new-dir/'])
725
700
 
726
701
    def test_register_diff(self):
727
702
        self.create_old_new()
728
 
        old_diff_factories = diff.DiffTree.diff_factories
729
 
        diff.DiffTree.diff_factories=old_diff_factories[:]
730
 
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
703
        old_diff_factories = DiffTree.diff_factories
 
704
        DiffTree.diff_factories=old_diff_factories[:]
 
705
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
731
706
        try:
732
 
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
707
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
733
708
        finally:
734
 
            diff.DiffTree.diff_factories = old_diff_factories
 
709
            DiffTree.diff_factories = old_diff_factories
735
710
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
736
711
        self.assertNotContainsRe(
737
712
            differ.to_file.getvalue(),
742
717
 
743
718
    def test_extra_factories(self):
744
719
        self.create_old_new()
745
 
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
746
 
                               extra_factories=[DiffWasIs.from_diff_tree])
 
720
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
 
721
                            extra_factories=[DiffWasIs.from_diff_tree])
747
722
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
748
723
        self.assertNotContainsRe(
749
724
            differ.to_file.getvalue(),
762
737
            '.*a-file(.|\n)*b-file')
763
738
 
764
739
 
765
 
class TestPatienceDiffLib(tests.TestCase):
 
740
class TestPatienceDiffLib(TestCase):
766
741
 
767
742
    def setUp(self):
768
743
        super(TestPatienceDiffLib, self).setUp()
769
 
        self._unique_lcs = _patiencediff_py.unique_lcs_py
770
 
        self._recurse_matches = _patiencediff_py.recurse_matches_py
 
744
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
 
745
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
771
746
        self._PatienceSequenceMatcher = \
772
 
            _patiencediff_py.PatienceSequenceMatcher_py
773
 
 
774
 
    def test_diff_unicode_string(self):
775
 
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
776
 
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
777
 
        sm = self._PatienceSequenceMatcher(None, a, b)
778
 
        mb = sm.get_matching_blocks()
779
 
        self.assertEquals(35, len(mb))
 
747
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
780
748
 
781
749
    def test_unique_lcs(self):
782
750
        unique_lcs = self._unique_lcs
788
756
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
789
757
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
790
758
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
791
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
 
759
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
792
760
                                                         (3,3), (4,4)])
793
761
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
794
762
 
809
777
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
810
778
                                          (4, 6), (5, 7), (6, 8)])
811
779
 
812
 
        # recurse_matches doesn't match non-unique
 
780
        # recurse_matches doesn't match non-unique 
813
781
        # lines surrounded by bogus text.
814
782
        # The update has been done in patiencediff.SequenceMatcher instead
815
783
 
952
920
                 ('delete', 1,2, 1,1),
953
921
                 ('equal',  2,3, 1,2),
954
922
                ])
955
 
        chk_ops('aBccDe', 'abccde',
 
923
        chk_ops('aBccDe', 'abccde', 
956
924
                [('equal',   0,1, 0,1),
957
925
                 ('replace', 1,5, 1,5),
958
926
                 ('equal',   5,6, 5,6),
959
927
                ])
960
 
        chk_ops('aBcDec', 'abcdec',
 
928
        chk_ops('aBcDec', 'abcdec', 
961
929
                [('equal',   0,1, 0,1),
962
930
                 ('replace', 1,2, 1,2),
963
931
                 ('equal',   2,3, 2,3),
964
932
                 ('replace', 3,4, 3,4),
965
933
                 ('equal',   4,6, 4,6),
966
934
                ])
967
 
        chk_ops('aBcdEcdFg', 'abcdecdfg',
 
935
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
968
936
                [('equal',   0,1, 0,1),
969
937
                 ('replace', 1,8, 1,8),
970
938
                 ('equal',   8,9, 8,9)
971
939
                ])
972
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
 
940
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
973
941
                [('equal',   0,1, 0,1),
974
942
                 ('replace', 1,2, 1,2),
975
943
                 ('equal',   2,4, 2,4),
1035
1003
    """
1036
1004
    gnxrf_netf = ['svyr*']
1037
1005
    gnxrf_bcgvbaf = ['ab-erphefr']
1038
 
 
 
1006
  
1039
1007
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1040
1008
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1041
1009
        vs vf_dhvrg():
1049
1017
'''.splitlines(True), '''\
1050
1018
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1051
1019
 
1052
 
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
 
1020
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
1053
1021
    nqq gurz.
1054
1022
    """
1055
1023
    gnxrf_netf = ['svyr*']
1082
1050
                 'how are you today?\n']
1083
1051
        txt_b = ['hello there\n',
1084
1052
                 'how are you today?\n']
1085
 
        unified_diff = patiencediff.unified_diff
 
1053
        unified_diff = bzrlib.patiencediff.unified_diff
1086
1054
        psm = self._PatienceSequenceMatcher
1087
 
        self.assertEquals(['--- \n',
1088
 
                           '+++ \n',
 
1055
        self.assertEquals([ '---  \n',
 
1056
                           '+++  \n',
1089
1057
                           '@@ -1,3 +1,2 @@\n',
1090
1058
                           ' hello there\n',
1091
1059
                           '-world\n',
1096
1064
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1097
1065
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1098
1066
        # This is the result with LongestCommonSubstring matching
1099
 
        self.assertEquals(['--- \n',
1100
 
                           '+++ \n',
 
1067
        self.assertEquals(['---  \n',
 
1068
                           '+++  \n',
1101
1069
                           '@@ -1,6 +1,11 @@\n',
1102
1070
                           ' a\n',
1103
1071
                           ' b\n',
1112
1080
                           ' f\n']
1113
1081
                          , list(unified_diff(txt_a, txt_b)))
1114
1082
        # And the patience diff
1115
 
        self.assertEquals(['--- \n',
1116
 
                           '+++ \n',
 
1083
        self.assertEquals(['---  \n',
 
1084
                           '+++  \n',
1117
1085
                           '@@ -4,6 +4,11 @@\n',
1118
1086
                           ' d\n',
1119
1087
                           ' e\n',
1130
1098
                          , list(unified_diff(txt_a, txt_b,
1131
1099
                                 sequencematcher=psm)))
1132
1100
 
1133
 
    def test_patience_unified_diff_with_dates(self):
1134
 
        txt_a = ['hello there\n',
1135
 
                 'world\n',
1136
 
                 'how are you today?\n']
1137
 
        txt_b = ['hello there\n',
1138
 
                 'how are you today?\n']
1139
 
        unified_diff = patiencediff.unified_diff
1140
 
        psm = self._PatienceSequenceMatcher
1141
 
        self.assertEquals(['--- a\t2008-08-08\n',
1142
 
                           '+++ b\t2008-09-09\n',
1143
 
                           '@@ -1,3 +1,2 @@\n',
1144
 
                           ' hello there\n',
1145
 
                           '-world\n',
1146
 
                           ' how are you today?\n'
1147
 
                          ]
1148
 
                          , list(unified_diff(txt_a, txt_b,
1149
 
                                 fromfile='a', tofile='b',
1150
 
                                 fromfiledate='2008-08-08',
1151
 
                                 tofiledate='2008-09-09',
1152
 
                                 sequencematcher=psm)))
1153
 
 
1154
1101
 
1155
1102
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1156
1103
 
1157
 
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1104
    _test_needs_features = [CompiledPatienceDiffFeature]
1158
1105
 
1159
1106
    def setUp(self):
1160
1107
        super(TestPatienceDiffLib_c, self).setUp()
1161
 
        from bzrlib import _patiencediff_c
1162
 
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1163
 
        self._recurse_matches = _patiencediff_c.recurse_matches_c
 
1108
        import bzrlib._patiencediff_c
 
1109
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
 
1110
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
1164
1111
        self._PatienceSequenceMatcher = \
1165
 
            _patiencediff_c.PatienceSequenceMatcher_c
 
1112
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1166
1113
 
1167
1114
    def test_unhashable(self):
1168
1115
        """We should get a proper exception here."""
1178
1125
                                         None, ['valid'], ['valid', []])
1179
1126
 
1180
1127
 
1181
 
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1128
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1182
1129
 
1183
1130
    def setUp(self):
1184
1131
        super(TestPatienceDiffLibFiles, self).setUp()
1185
1132
        self._PatienceSequenceMatcher = \
1186
 
            _patiencediff_py.PatienceSequenceMatcher_py
 
1133
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1187
1134
 
1188
1135
    def test_patience_unified_diff_files(self):
1189
1136
        txt_a = ['hello there\n',
1194
1141
        open('a1', 'wb').writelines(txt_a)
1195
1142
        open('b1', 'wb').writelines(txt_b)
1196
1143
 
1197
 
        unified_diff_files = patiencediff.unified_diff_files
 
1144
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1198
1145
        psm = self._PatienceSequenceMatcher
1199
 
        self.assertEquals(['--- a1\n',
1200
 
                           '+++ b1\n',
 
1146
        self.assertEquals(['--- a1 \n',
 
1147
                           '+++ b1 \n',
1201
1148
                           '@@ -1,3 +1,2 @@\n',
1202
1149
                           ' hello there\n',
1203
1150
                           '-world\n',
1212
1159
        open('b2', 'wb').writelines(txt_b)
1213
1160
 
1214
1161
        # This is the result with LongestCommonSubstring matching
1215
 
        self.assertEquals(['--- a2\n',
1216
 
                           '+++ b2\n',
 
1162
        self.assertEquals(['--- a2 \n',
 
1163
                           '+++ b2 \n',
1217
1164
                           '@@ -1,6 +1,11 @@\n',
1218
1165
                           ' a\n',
1219
1166
                           ' b\n',
1229
1176
                          , list(unified_diff_files('a2', 'b2')))
1230
1177
 
1231
1178
        # And the patience diff
1232
 
        self.assertEquals(['--- a2\n',
1233
 
                           '+++ b2\n',
 
1179
        self.assertEquals(['--- a2 \n',
 
1180
                           '+++ b2 \n',
1234
1181
                           '@@ -4,6 +4,11 @@\n',
1235
1182
                           ' d\n',
1236
1183
                           ' e\n',
1250
1197
 
1251
1198
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1252
1199
 
1253
 
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1200
    _test_needs_features = [CompiledPatienceDiffFeature]
1254
1201
 
1255
1202
    def setUp(self):
1256
1203
        super(TestPatienceDiffLibFiles_c, self).setUp()
1257
 
        from bzrlib import _patiencediff_c
 
1204
        import bzrlib._patiencediff_c
1258
1205
        self._PatienceSequenceMatcher = \
1259
 
            _patiencediff_c.PatienceSequenceMatcher_c
1260
 
 
1261
 
 
1262
 
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1206
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1207
 
 
1208
 
 
1209
class TestUsingCompiledIfAvailable(TestCase):
1263
1210
 
1264
1211
    def test_PatienceSequenceMatcher(self):
1265
 
        if features.compiled_patiencediff_feature.available():
 
1212
        if CompiledPatienceDiffFeature.available():
1266
1213
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1267
1214
            self.assertIs(PatienceSequenceMatcher_c,
1268
 
                          patiencediff.PatienceSequenceMatcher)
 
1215
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1269
1216
        else:
1270
1217
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1271
1218
            self.assertIs(PatienceSequenceMatcher_py,
1272
 
                          patiencediff.PatienceSequenceMatcher)
 
1219
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1273
1220
 
1274
1221
    def test_unique_lcs(self):
1275
 
        if features.compiled_patiencediff_feature.available():
 
1222
        if CompiledPatienceDiffFeature.available():
1276
1223
            from bzrlib._patiencediff_c import unique_lcs_c
1277
1224
            self.assertIs(unique_lcs_c,
1278
 
                          patiencediff.unique_lcs)
 
1225
                          bzrlib.patiencediff.unique_lcs)
1279
1226
        else:
1280
1227
            from bzrlib._patiencediff_py import unique_lcs_py
1281
1228
            self.assertIs(unique_lcs_py,
1282
 
                          patiencediff.unique_lcs)
 
1229
                          bzrlib.patiencediff.unique_lcs)
1283
1230
 
1284
1231
    def test_recurse_matches(self):
1285
 
        if features.compiled_patiencediff_feature.available():
 
1232
        if CompiledPatienceDiffFeature.available():
1286
1233
            from bzrlib._patiencediff_c import recurse_matches_c
1287
1234
            self.assertIs(recurse_matches_c,
1288
 
                          patiencediff.recurse_matches)
 
1235
                          bzrlib.patiencediff.recurse_matches)
1289
1236
        else:
1290
1237
            from bzrlib._patiencediff_py import recurse_matches_py
1291
1238
            self.assertIs(recurse_matches_py,
1292
 
                          patiencediff.recurse_matches)
1293
 
 
1294
 
 
1295
 
class TestDiffFromTool(tests.TestCaseWithTransport):
 
1239
                          bzrlib.patiencediff.recurse_matches)
 
1240
 
 
1241
 
 
1242
class TestDiffFromTool(TestCaseWithTransport):
1296
1243
 
1297
1244
    def test_from_string(self):
1298
 
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
 
1245
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1299
1246
        self.addCleanup(diff_obj.finish)
1300
 
        self.assertEqual(['diff', '@old_path', '@new_path'],
 
1247
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1301
1248
            diff_obj.command_template)
1302
1249
 
1303
1250
    def test_from_string_u5(self):
1304
 
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1305
 
                                                 None, None, None)
 
1251
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1306
1252
        self.addCleanup(diff_obj.finish)
1307
 
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
 
1253
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1308
1254
                         diff_obj.command_template)
1309
1255
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1310
1256
                         diff_obj._get_command('old-path', 'new-path'))
1311
1257
 
1312
 
    def test_from_string_path_with_backslashes(self):
1313
 
        self.requireFeature(features.backslashdir_feature)
1314
 
        tool = 'C:\\Tools\\Diff.exe'
1315
 
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1316
 
        self.addCleanup(diff_obj.finish)
1317
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
1318
 
                         diff_obj.command_template)
1319
 
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1320
 
                         diff_obj._get_command('old-path', 'new-path'))
1321
 
 
1322
1258
    def test_execute(self):
1323
1259
        output = StringIO()
1324
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1325
 
                                      'print "@old_path @new_path"'],
1326
 
                                     None, None, output)
 
1260
        diff_obj = DiffFromTool(['python', '-c',
 
1261
                                 'print "%(old_path)s %(new_path)s"'],
 
1262
                                None, None, output)
1327
1263
        self.addCleanup(diff_obj.finish)
1328
1264
        diff_obj._execute('old', 'new')
1329
1265
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1330
1266
 
1331
1267
    def test_excute_missing(self):
1332
 
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1333
 
                                     None, None, None)
 
1268
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1269
                                None, None, None)
1334
1270
        self.addCleanup(diff_obj.finish)
1335
 
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1336
 
                              'old', 'new')
 
1271
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
 
1272
                              'new')
1337
1273
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1338
1274
                         ' on this machine', str(e))
1339
1275
 
1340
 
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1341
 
        self.requireFeature(features.AttribFeature)
1342
 
        output = StringIO()
1343
 
        tree = self.make_branch_and_tree('tree')
1344
 
        self.build_tree_contents([('tree/file', 'content')])
1345
 
        tree.add('file', 'file-id')
1346
 
        tree.commit('old tree')
1347
 
        tree.lock_read()
1348
 
        self.addCleanup(tree.unlock)
1349
 
        basis_tree = tree.basis_tree()
1350
 
        basis_tree.lock_read()
1351
 
        self.addCleanup(basis_tree.unlock)
1352
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1353
 
                                      'print "@old_path @new_path"'],
1354
 
                                     basis_tree, tree, output)
1355
 
        diff_obj._prepare_files('file-id', 'file', 'file')
1356
 
        # The old content should be readonly
1357
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1358
 
                                    r'R.*old\\file$')
1359
 
        # The new content should use the tree object, not a 'new' file anymore
1360
 
        self.assertEndsWith(tree.basedir, 'work/tree')
1361
 
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1362
 
 
1363
 
    def assertReadableByAttrib(self, cwd, relpath, regex):
1364
 
        proc = subprocess.Popen(['attrib', relpath],
1365
 
                                stdout=subprocess.PIPE,
1366
 
                                cwd=cwd)
1367
 
        (result, err) = proc.communicate()
1368
 
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1369
 
 
1370
1276
    def test_prepare_files(self):
1371
1277
        output = StringIO()
1372
1278
        tree = self.make_branch_and_tree('tree')
1373
1279
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1374
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1375
1280
        tree.add('oldname', 'file-id')
1376
 
        tree.add('oldname2', 'file2-id')
1377
 
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1378
 
        tree.commit('old tree', timestamp=315532800)
 
1281
        tree.commit('old tree', timestamp=0)
1379
1282
        tree.rename_one('oldname', 'newname')
1380
 
        tree.rename_one('oldname2', 'newname2')
1381
1283
        self.build_tree_contents([('tree/newname', 'newcontent')])
1382
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1383
1284
        old_tree = tree.basis_tree()
1384
1285
        old_tree.lock_read()
1385
1286
        self.addCleanup(old_tree.unlock)
1386
1287
        tree.lock_read()
1387
1288
        self.addCleanup(tree.unlock)
1388
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1389
 
                                      'print "@old_path @new_path"'],
1390
 
                                     old_tree, tree, output)
 
1289
        diff_obj = DiffFromTool(['python', '-c',
 
1290
                                 'print "%(old_path)s %(new_path)s"'],
 
1291
                                old_tree, tree, output)
1391
1292
        self.addCleanup(diff_obj.finish)
1392
1293
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1393
1294
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1394
1295
                                                     'newname')
1395
1296
        self.assertContainsRe(old_path, 'old/oldname$')
1396
 
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1397
 
        self.assertContainsRe(new_path, 'tree/newname$')
 
1297
        self.assertEqual(0, os.stat(old_path).st_mtime)
 
1298
        self.assertContainsRe(new_path, 'new/newname$')
1398
1299
        self.assertFileEqual('oldcontent', old_path)
1399
1300
        self.assertFileEqual('newcontent', new_path)
1400
 
        if osutils.host_os_dereferences_symlinks():
 
1301
        if osutils.has_symlinks():
1401
1302
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1402
1303
        # make sure we can create files with the same parent directories
1403
 
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1404
 
 
1405
 
 
1406
 
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1407
 
 
1408
 
    def test_encodable_filename(self):
1409
 
        # Just checks file path for external diff tool.
1410
 
        # We cannot change CPython's internal encoding used by os.exec*.
1411
 
        import sys
1412
 
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1413
 
                                    None, None, None)
1414
 
        for _, scenario in EncodingAdapter.encoding_scenarios:
1415
 
            encoding = scenario['encoding']
1416
 
            dirname  = scenario['info']['directory']
1417
 
            filename = scenario['info']['filename']
1418
 
 
1419
 
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1420
 
            relpath = dirname + u'/' + filename
1421
 
            fullpath = diffobj._safe_filename('safe', relpath)
1422
 
            self.assertEqual(
1423
 
                    fullpath,
1424
 
                    fullpath.encode(encoding).decode(encoding)
1425
 
                    )
1426
 
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
1427
 
 
1428
 
    def test_unencodable_filename(self):
1429
 
        import sys
1430
 
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1431
 
                                    None, None, None)
1432
 
        for _, scenario in EncodingAdapter.encoding_scenarios:
1433
 
            encoding = scenario['encoding']
1434
 
            dirname  = scenario['info']['directory']
1435
 
            filename = scenario['info']['filename']
1436
 
 
1437
 
            if encoding == 'iso-8859-1':
1438
 
                encoding = 'iso-8859-2'
1439
 
            else:
1440
 
                encoding = 'iso-8859-1'
1441
 
 
1442
 
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1443
 
            relpath = dirname + u'/' + filename
1444
 
            fullpath = diffobj._safe_filename('safe', relpath)
1445
 
            self.assertEqual(
1446
 
                    fullpath,
1447
 
                    fullpath.encode(encoding).decode(encoding)
1448
 
                    )
1449
 
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
1450
 
 
1451
 
 
1452
 
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1453
 
 
1454
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1455
 
        """Call get_trees_and_branches_to_diff_locked."""
1456
 
        return diff.get_trees_and_branches_to_diff_locked(
1457
 
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1458
 
 
1459
 
    def test_basic(self):
1460
 
        tree = self.make_branch_and_tree('tree')
1461
 
        (old_tree, new_tree,
1462
 
         old_branch, new_branch,
1463
 
         specific_files, extra_trees) = self.call_gtabtd(
1464
 
             ['tree'], None, None, None)
1465
 
 
1466
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1467
 
        self.assertEqual(_mod_revision.NULL_REVISION,
1468
 
                         old_tree.get_revision_id())
1469
 
        self.assertEqual(tree.basedir, new_tree.basedir)
1470
 
        self.assertEqual(tree.branch.base, old_branch.base)
1471
 
        self.assertEqual(tree.branch.base, new_branch.base)
1472
 
        self.assertIs(None, specific_files)
1473
 
        self.assertIs(None, extra_trees)
1474
 
 
1475
 
    def test_with_rev_specs(self):
1476
 
        tree = self.make_branch_and_tree('tree')
1477
 
        self.build_tree_contents([('tree/file', 'oldcontent')])
1478
 
        tree.add('file', 'file-id')
1479
 
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1480
 
        self.build_tree_contents([('tree/file', 'newcontent')])
1481
 
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1482
 
 
1483
 
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1484
 
                     revisionspec.RevisionSpec.from_string('2')]
1485
 
        (old_tree, new_tree,
1486
 
         old_branch, new_branch,
1487
 
         specific_files, extra_trees) = self.call_gtabtd(
1488
 
            ['tree'], revisions, None, None)
1489
 
 
1490
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1491
 
        self.assertEqual("old-id", old_tree.get_revision_id())
1492
 
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1493
 
        self.assertEqual("new-id", new_tree.get_revision_id())
1494
 
        self.assertEqual(tree.branch.base, old_branch.base)
1495
 
        self.assertEqual(tree.branch.base, new_branch.base)
1496
 
        self.assertIs(None, specific_files)
1497
 
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
 
1304
        diff_obj._prepare_files('file-id', 'oldname2', 'newname2')