~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: 2010-11-11 08:45:19 UTC
  • mfrom: (4597.9.22 reports-conflict-resolved)
  • Revision ID: pqm@pqm.ubuntu.com-20101111084519-bmk1zmblp7kex41a
(vila) More feedback about the conflicts just resolved and the remaining
 ones. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import os.path
19
18
from cStringIO import StringIO
20
 
import errno
21
19
import subprocess
22
 
from tempfile import TemporaryFile
 
20
import sys
 
21
import tempfile
23
22
 
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,
 
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
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):
 
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):
44
41
 
45
42
    def _probe(self):
 
43
        if (sys.platform not in ('cygwin', 'win32')):
 
44
            return False
46
45
        try:
47
 
            import bzrlib._patiencediff_c
48
 
        except ImportError:
 
46
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
 
47
        except OSError, e:
49
48
            return False
50
 
        return True
 
49
        return (0 == proc.wait())
51
50
 
52
51
    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
 
        
 
52
        return 'attrib Windows command-line tool'
 
53
 
 
54
AttribFeature = _AttribFeature()
 
55
 
 
56
 
 
57
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
 
58
                                    'bzrlib._patiencediff_c')
 
59
 
86
60
 
87
61
def udiff_lines(old, new, allow_binary=False):
88
62
    output = StringIO()
89
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
63
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
90
64
    output.seek(0, 0)
91
65
    return output.readlines()
92
66
 
96
70
        # StringIO has no fileno, so it tests a different codepath
97
71
        output = StringIO()
98
72
    else:
99
 
        output = TemporaryFile()
 
73
        output = tempfile.TemporaryFile()
100
74
    try:
101
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
102
 
    except NoDiff:
103
 
        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')
104
78
    output.seek(0, 0)
105
79
    lines = output.readlines()
106
80
    output.close()
107
81
    return lines
108
82
 
109
83
 
110
 
class TestDiff(TestCase):
 
84
class TestDiff(tests.TestCase):
111
85
 
112
86
    def test_add_nl(self):
113
87
        """diff generates a valid diff for patches that add a newline"""
149
123
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
150
124
 
151
125
    def test_binary_lines(self):
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)
 
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)
156
132
 
157
133
    def test_external_diff(self):
158
134
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
188
164
        orig_path = os.environ['PATH']
189
165
        try:
190
166
            os.environ['PATH'] = ''
191
 
            self.assertRaises(NoDiff, external_diff,
 
167
            self.assertRaises(errors.NoDiff, diff.external_diff,
192
168
                              'old', ['boo\n'], 'new', ['goo\n'],
193
169
                              StringIO(), diff_opts=['-u'])
194
170
        finally:
195
171
            os.environ['PATH'] = orig_path
196
 
        
 
172
 
197
173
    def test_internal_diff_default(self):
198
174
        # Default internal diff encoding is utf8
199
175
        output = StringIO()
200
 
        internal_diff(u'old_\xb5', ['old_text\n'],
201
 
                    u'new_\xe5', ['new_text\n'], output)
 
176
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
177
                           u'new_\xe5', ['new_text\n'], output)
202
178
        lines = output.getvalue().splitlines(True)
203
179
        self.check_patch(lines)
204
180
        self.assertEquals(['--- old_\xc2\xb5\n',
212
188
 
213
189
    def test_internal_diff_utf8(self):
214
190
        output = StringIO()
215
 
        internal_diff(u'old_\xb5', ['old_text\n'],
216
 
                    u'new_\xe5', ['new_text\n'], output,
217
 
                    path_encoding='utf8')
 
191
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
192
                           u'new_\xe5', ['new_text\n'], output,
 
193
                           path_encoding='utf8')
218
194
        lines = output.getvalue().splitlines(True)
219
195
        self.check_patch(lines)
220
196
        self.assertEquals(['--- old_\xc2\xb5\n',
228
204
 
229
205
    def test_internal_diff_iso_8859_1(self):
230
206
        output = StringIO()
231
 
        internal_diff(u'old_\xb5', ['old_text\n'],
232
 
                    u'new_\xe5', ['new_text\n'], output,
233
 
                    path_encoding='iso-8859-1')
 
207
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
208
                           u'new_\xe5', ['new_text\n'], output,
 
209
                           path_encoding='iso-8859-1')
234
210
        lines = output.getvalue().splitlines(True)
235
211
        self.check_patch(lines)
236
212
        self.assertEquals(['--- old_\xb5\n',
244
220
 
245
221
    def test_internal_diff_no_content(self):
246
222
        output = StringIO()
247
 
        internal_diff(u'old', [], u'new', [], output)
 
223
        diff.internal_diff(u'old', [], u'new', [], output)
248
224
        self.assertEqual('', output.getvalue())
249
225
 
250
226
    def test_internal_diff_no_changes(self):
251
227
        output = StringIO()
252
 
        internal_diff(u'old', ['text\n', 'contents\n'],
253
 
                      u'new', ['text\n', 'contents\n'],
254
 
                      output)
 
228
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
229
                           u'new', ['text\n', 'contents\n'],
 
230
                           output)
255
231
        self.assertEqual('', output.getvalue())
256
232
 
257
233
    def test_internal_diff_returns_bytes(self):
258
234
        import StringIO
259
235
        output = StringIO.StringIO()
260
 
        internal_diff(u'old_\xb5', ['old_text\n'],
261
 
                    u'new_\xe5', ['new_text\n'], output)
 
236
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
237
                            u'new_\xe5', ['new_text\n'], output)
262
238
        self.failUnless(isinstance(output.getvalue(), str),
263
239
            'internal_diff should return bytestrings')
264
240
 
265
241
 
266
 
class TestDiffFiles(TestCaseInTempDir):
 
242
class TestDiffFiles(tests.TestCaseInTempDir):
267
243
 
268
244
    def test_external_diff_binary(self):
269
245
        """The output when using external diff should use diff's i18n error"""
282
258
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
283
259
 
284
260
 
285
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
 
261
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
286
262
    """Has a helper for running show_diff_trees"""
287
263
 
288
264
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
291
267
            extra_trees = (working_tree,)
292
268
        else:
293
269
            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/')
 
270
        diff.show_diff_trees(tree1, tree2, output,
 
271
                             specific_files=specific_files,
 
272
                             extra_trees=extra_trees, old_label='old/',
 
273
                             new_label='new/')
297
274
        return output.getvalue()
298
275
 
299
276
 
364
341
+file2 contents at rev 3
365
342
 
366
343
''')
367
 
        
 
344
 
368
345
    def test_diff_add_files(self):
369
 
        tree1 = self.b.repository.revision_tree(None)
 
346
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
370
347
        tree2 = self.b.repository.revision_tree('rev-1')
371
348
        output = self.get_diff(tree1, tree2)
372
349
        # the files have the epoch time stamp for the tree in which
406
383
        self.wt.rename_one('file1', 'file1b')
407
384
        old_tree = self.b.repository.revision_tree('rev-1')
408
385
        new_tree = self.b.repository.revision_tree('rev-4')
409
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
 
386
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
410
387
                            working_tree=self.wt)
411
388
        self.assertContainsRe(out, 'file1\t')
412
389
 
418
395
        self.wt.rename_one('file1', 'dir1/file1')
419
396
        old_tree = self.b.repository.revision_tree('rev-1')
420
397
        new_tree = self.b.repository.revision_tree('rev-4')
421
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
 
398
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
422
399
                            working_tree=self.wt)
423
400
        self.assertContainsRe(out, 'file1\t')
424
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
 
401
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
425
402
                            working_tree=self.wt)
426
403
        self.assertNotContainsRe(out, 'file1\t')
427
404
 
438
415
        tree.commit('one', rev_id='rev-1')
439
416
 
440
417
        self.build_tree_contents([('tree/file', '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')
 
418
        d = self.get_diff(tree.basis_tree(), tree)
 
419
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
420
        self.assertContainsRe(d, '--- old/file\t')
 
421
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
422
        self.assertContainsRe(d, '-contents\n'
 
423
                                 '\\+new contents\n')
447
424
 
448
425
    def test_modified_file_in_renamed_dir(self):
449
426
        """Test when a file is modified in a renamed directory."""
455
432
 
456
433
        tree.rename_one('dir', 'other')
457
434
        self.build_tree_contents([('tree/other/file', 'new contents\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")
 
435
        d = self.get_diff(tree.basis_tree(), tree)
 
436
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
437
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
461
438
        # XXX: This is technically incorrect, because it used to be at another
462
439
        # location. What to do?
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')
 
440
        self.assertContainsRe(d, '--- old/dir/file\t')
 
441
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
442
        self.assertContainsRe(d, '-contents\n'
 
443
                                 '\\+new contents\n')
467
444
 
468
445
    def test_renamed_directory(self):
469
446
        """Test when only a directory is only renamed."""
474
451
        tree.commit('one', rev_id='rev-1')
475
452
 
476
453
        tree.rename_one('dir', 'newdir')
477
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
454
        d = self.get_diff(tree.basis_tree(), tree)
478
455
        # Renaming a directory should be a single "you renamed this dir" even
479
456
        # when there are files inside.
480
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
457
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
481
458
 
482
459
    def test_renamed_file(self):
483
460
        """Test when a file is only renamed."""
487
464
        tree.commit('one', rev_id='rev-1')
488
465
 
489
466
        tree.rename_one('file', 'newname')
490
 
        diff = self.get_diff(tree.basis_tree(), tree)
491
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
467
        d = self.get_diff(tree.basis_tree(), tree)
 
468
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
492
469
        # We shouldn't have a --- or +++ line, because there is no content
493
470
        # change
494
 
        self.assertNotContainsRe(diff, '---')
 
471
        self.assertNotContainsRe(d, '---')
495
472
 
496
473
    def test_renamed_and_modified_file(self):
497
474
        """Test when a file is only renamed."""
502
479
 
503
480
        tree.rename_one('file', 'newname')
504
481
        self.build_tree_contents([('tree/newname', 'new contents\n')])
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')
 
482
        d = self.get_diff(tree.basis_tree(), tree)
 
483
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
484
        self.assertContainsRe(d, '--- old/file\t')
 
485
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
486
        self.assertContainsRe(d, '-contents\n'
 
487
                                 '\\+new contents\n')
 
488
 
 
489
 
 
490
    def test_internal_diff_exec_property(self):
 
491
        tree = self.make_branch_and_tree('tree')
 
492
 
 
493
        tt = transform.TreeTransform(tree)
 
494
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
 
495
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
 
496
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
 
497
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
 
498
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
 
499
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
 
500
        tt.apply()
 
501
        tree.commit('one', rev_id='rev-1')
 
502
 
 
503
        tt = transform.TreeTransform(tree)
 
504
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
 
505
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
 
506
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
 
507
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
 
508
        tt.apply()
 
509
        tree.rename_one('c', 'new-c')
 
510
        tree.rename_one('d', 'new-d')
 
511
 
 
512
        d = self.get_diff(tree.basis_tree(), tree)
 
513
 
 
514
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
515
                                  ".*\+x to -x.*\)")
 
516
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
517
                                  ".*-x to \+x.*\)")
 
518
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
519
                                  ".*\+x to -x.*\)")
 
520
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
521
                                  ".*-x to \+x.*\)")
 
522
        self.assertNotContainsRe(d, r"file 'e'")
 
523
        self.assertNotContainsRe(d, r"file 'f'")
511
524
 
512
525
    def test_binary_unicode_filenames(self):
513
526
        """Test that contents of files are *not* encoded in UTF-8 when there
514
527
        is a binary file in the diff.
515
528
        """
516
529
        # See https://bugs.launchpad.net/bugs/110092.
517
 
        self.requireFeature(UnicodeFilename)
 
530
        self.requireFeature(tests.UnicodeFilenameFeature)
518
531
 
519
532
        # This bug isn't triggered with cStringIO.
520
533
        from StringIO import StringIO
528
541
        tree.add([alpha], ['file-id'])
529
542
        tree.add([omega], ['file-id-2'])
530
543
        diff_content = StringIO()
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,))
 
544
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
 
545
        d = diff_content.getvalue()
 
546
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
 
547
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
548
                              % (alpha_utf8, alpha_utf8))
 
549
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
 
550
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
 
551
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
539
552
 
540
553
    def test_unicode_filename(self):
541
554
        """Test when the filename are unicode."""
542
 
        self.requireFeature(UnicodeFilename)
 
555
        self.requireFeature(tests.UnicodeFilenameFeature)
543
556
 
544
557
        alpha, omega = u'\u03b1', u'\u03c9'
545
558
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
560
573
        tree.add(['add_'+alpha], ['file-id'])
561
574
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
562
575
 
563
 
        diff = self.get_diff(tree.basis_tree(), tree)
564
 
        self.assertContainsRe(diff,
 
576
        d = self.get_diff(tree.basis_tree(), tree)
 
577
        self.assertContainsRe(d,
565
578
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
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):
 
579
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
 
580
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
 
581
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
 
582
 
 
583
    def test_unicode_filename_path_encoding(self):
 
584
        """Test for bug #382699: unicode filenames on Windows should be shown
 
585
        in user encoding.
 
586
        """
 
587
        self.requireFeature(tests.UnicodeFilenameFeature)
 
588
        # The word 'test' in Russian
 
589
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
590
        directory = _russian_test + u'/'
 
591
        test_txt = _russian_test + u'.txt'
 
592
        u1234 = u'\u1234.txt'
 
593
 
 
594
        tree = self.make_branch_and_tree('.')
 
595
        self.build_tree_contents([
 
596
            (test_txt, 'foo\n'),
 
597
            (u1234, 'foo\n'),
 
598
            (directory, None),
 
599
            ])
 
600
        tree.add([test_txt, u1234, directory])
 
601
 
 
602
        sio = StringIO()
 
603
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
604
            path_encoding='cp1251')
 
605
 
 
606
        output = subst_dates(sio.getvalue())
 
607
        shouldbe = ('''\
 
608
=== added directory '%(directory)s'
 
609
=== added file '%(test_txt)s'
 
610
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
611
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
612
@@ -0,0 +1,1 @@
 
613
+foo
 
614
 
 
615
=== added file '?.txt'
 
616
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
617
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
618
@@ -0,0 +1,1 @@
 
619
+foo
 
620
 
 
621
''' % {'directory': _russian_test.encode('cp1251'),
 
622
       'test_txt': test_txt.encode('cp1251'),
 
623
      })
 
624
        self.assertEqualDiff(output, shouldbe)
 
625
 
 
626
 
 
627
class DiffWasIs(diff.DiffPath):
572
628
 
573
629
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
574
630
        self.to_file.write('was: ')
578
634
        pass
579
635
 
580
636
 
581
 
class TestDiffTree(TestCaseWithTransport):
 
637
class TestDiffTree(tests.TestCaseWithTransport):
582
638
 
583
639
    def setUp(self):
584
 
        TestCaseWithTransport.setUp(self)
 
640
        super(TestDiffTree, self).setUp()
585
641
        self.old_tree = self.make_branch_and_tree('old-tree')
586
642
        self.old_tree.lock_write()
587
643
        self.addCleanup(self.old_tree.unlock)
588
644
        self.new_tree = self.make_branch_and_tree('new-tree')
589
645
        self.new_tree.lock_write()
590
646
        self.addCleanup(self.new_tree.unlock)
591
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
647
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
592
648
 
593
649
    def test_diff_text(self):
594
650
        self.build_tree_contents([('old-tree/olddir/',),
599
655
                                  ('new-tree/newdir/newfile', 'new\n')])
600
656
        self.new_tree.add('newdir')
601
657
        self.new_tree.add('newdir/newfile', 'file-id')
602
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
658
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
603
659
        differ.diff_text('file-id', None, 'old label', 'new label')
604
660
        self.assertEqual(
605
661
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
634
690
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
635
691
 
636
692
    def test_diff_symlink(self):
637
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
693
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
638
694
        differ.diff_symlink('old target', None)
639
695
        self.assertEqual("=== target was 'old target'\n",
640
696
                         differ.to_file.getvalue())
641
697
 
642
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
698
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
643
699
        differ.diff_symlink(None, 'new target')
644
700
        self.assertEqual("=== target is 'new target'\n",
645
701
                         differ.to_file.getvalue())
646
702
 
647
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
703
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
648
704
        differ.diff_symlink('old target', 'new target')
649
705
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
650
706
                         differ.to_file.getvalue())
680
736
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
681
737
             ' \@\@\n-old\n\n')
682
738
        self.assertContainsRe(self.differ.to_file.getvalue(),
683
 
                              "=== target is 'new'\n")
 
739
                              "=== target is u'new'\n")
684
740
 
685
741
    def test_diff_directory(self):
686
742
        self.build_tree(['new-tree/new-dir/'])
700
756
 
701
757
    def test_register_diff(self):
702
758
        self.create_old_new()
703
 
        old_diff_factories = DiffTree.diff_factories
704
 
        DiffTree.diff_factories=old_diff_factories[:]
705
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
759
        old_diff_factories = diff.DiffTree.diff_factories
 
760
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
761
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
706
762
        try:
707
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
763
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
708
764
        finally:
709
 
            DiffTree.diff_factories = old_diff_factories
 
765
            diff.DiffTree.diff_factories = old_diff_factories
710
766
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
711
767
        self.assertNotContainsRe(
712
768
            differ.to_file.getvalue(),
717
773
 
718
774
    def test_extra_factories(self):
719
775
        self.create_old_new()
720
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
721
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
776
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
777
                               extra_factories=[DiffWasIs.from_diff_tree])
722
778
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
723
779
        self.assertNotContainsRe(
724
780
            differ.to_file.getvalue(),
737
793
            '.*a-file(.|\n)*b-file')
738
794
 
739
795
 
740
 
class TestPatienceDiffLib(TestCase):
 
796
class TestPatienceDiffLib(tests.TestCase):
741
797
 
742
798
    def setUp(self):
743
799
        super(TestPatienceDiffLib, self).setUp()
744
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
745
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
 
800
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
801
        self._recurse_matches = _patiencediff_py.recurse_matches_py
746
802
        self._PatienceSequenceMatcher = \
747
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
803
            _patiencediff_py.PatienceSequenceMatcher_py
 
804
 
 
805
    def test_diff_unicode_string(self):
 
806
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
 
807
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
 
808
        sm = self._PatienceSequenceMatcher(None, a, b)
 
809
        mb = sm.get_matching_blocks()
 
810
        self.assertEquals(35, len(mb))
748
811
 
749
812
    def test_unique_lcs(self):
750
813
        unique_lcs = self._unique_lcs
756
819
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
757
820
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
758
821
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
759
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
 
822
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
760
823
                                                         (3,3), (4,4)])
761
824
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
762
825
 
777
840
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
778
841
                                          (4, 6), (5, 7), (6, 8)])
779
842
 
780
 
        # recurse_matches doesn't match non-unique 
 
843
        # recurse_matches doesn't match non-unique
781
844
        # lines surrounded by bogus text.
782
845
        # The update has been done in patiencediff.SequenceMatcher instead
783
846
 
920
983
                 ('delete', 1,2, 1,1),
921
984
                 ('equal',  2,3, 1,2),
922
985
                ])
923
 
        chk_ops('aBccDe', 'abccde', 
 
986
        chk_ops('aBccDe', 'abccde',
924
987
                [('equal',   0,1, 0,1),
925
988
                 ('replace', 1,5, 1,5),
926
989
                 ('equal',   5,6, 5,6),
927
990
                ])
928
 
        chk_ops('aBcDec', 'abcdec', 
 
991
        chk_ops('aBcDec', 'abcdec',
929
992
                [('equal',   0,1, 0,1),
930
993
                 ('replace', 1,2, 1,2),
931
994
                 ('equal',   2,3, 2,3),
932
995
                 ('replace', 3,4, 3,4),
933
996
                 ('equal',   4,6, 4,6),
934
997
                ])
935
 
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
 
998
        chk_ops('aBcdEcdFg', 'abcdecdfg',
936
999
                [('equal',   0,1, 0,1),
937
1000
                 ('replace', 1,8, 1,8),
938
1001
                 ('equal',   8,9, 8,9)
939
1002
                ])
940
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
 
1003
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
941
1004
                [('equal',   0,1, 0,1),
942
1005
                 ('replace', 1,2, 1,2),
943
1006
                 ('equal',   2,4, 2,4),
1003
1066
    """
1004
1067
    gnxrf_netf = ['svyr*']
1005
1068
    gnxrf_bcgvbaf = ['ab-erphefr']
1006
 
  
 
1069
 
1007
1070
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1008
1071
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1009
1072
        vs vf_dhvrg():
1017
1080
'''.splitlines(True), '''\
1018
1081
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1019
1082
 
1020
 
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
 
1083
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1021
1084
    nqq gurz.
1022
1085
    """
1023
1086
    gnxrf_netf = ['svyr*']
1050
1113
                 'how are you today?\n']
1051
1114
        txt_b = ['hello there\n',
1052
1115
                 'how are you today?\n']
1053
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1116
        unified_diff = patiencediff.unified_diff
1054
1117
        psm = self._PatienceSequenceMatcher
1055
 
        self.assertEquals([ '---  \n',
1056
 
                           '+++  \n',
 
1118
        self.assertEquals(['--- \n',
 
1119
                           '+++ \n',
1057
1120
                           '@@ -1,3 +1,2 @@\n',
1058
1121
                           ' hello there\n',
1059
1122
                           '-world\n',
1064
1127
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1065
1128
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1066
1129
        # This is the result with LongestCommonSubstring matching
1067
 
        self.assertEquals(['---  \n',
1068
 
                           '+++  \n',
 
1130
        self.assertEquals(['--- \n',
 
1131
                           '+++ \n',
1069
1132
                           '@@ -1,6 +1,11 @@\n',
1070
1133
                           ' a\n',
1071
1134
                           ' b\n',
1080
1143
                           ' f\n']
1081
1144
                          , list(unified_diff(txt_a, txt_b)))
1082
1145
        # And the patience diff
1083
 
        self.assertEquals(['---  \n',
1084
 
                           '+++  \n',
 
1146
        self.assertEquals(['--- \n',
 
1147
                           '+++ \n',
1085
1148
                           '@@ -4,6 +4,11 @@\n',
1086
1149
                           ' d\n',
1087
1150
                           ' e\n',
1098
1161
                          , list(unified_diff(txt_a, txt_b,
1099
1162
                                 sequencematcher=psm)))
1100
1163
 
 
1164
    def test_patience_unified_diff_with_dates(self):
 
1165
        txt_a = ['hello there\n',
 
1166
                 'world\n',
 
1167
                 'how are you today?\n']
 
1168
        txt_b = ['hello there\n',
 
1169
                 'how are you today?\n']
 
1170
        unified_diff = patiencediff.unified_diff
 
1171
        psm = self._PatienceSequenceMatcher
 
1172
        self.assertEquals(['--- a\t2008-08-08\n',
 
1173
                           '+++ b\t2008-09-09\n',
 
1174
                           '@@ -1,3 +1,2 @@\n',
 
1175
                           ' hello there\n',
 
1176
                           '-world\n',
 
1177
                           ' how are you today?\n'
 
1178
                          ]
 
1179
                          , list(unified_diff(txt_a, txt_b,
 
1180
                                 fromfile='a', tofile='b',
 
1181
                                 fromfiledate='2008-08-08',
 
1182
                                 tofiledate='2008-09-09',
 
1183
                                 sequencematcher=psm)))
 
1184
 
1101
1185
 
1102
1186
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1103
1187
 
1104
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1188
    _test_needs_features = [compiled_patiencediff_feature]
1105
1189
 
1106
1190
    def setUp(self):
1107
1191
        super(TestPatienceDiffLib_c, self).setUp()
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
 
1192
        from bzrlib import _patiencediff_c
 
1193
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1194
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1111
1195
        self._PatienceSequenceMatcher = \
1112
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1196
            _patiencediff_c.PatienceSequenceMatcher_c
1113
1197
 
1114
1198
    def test_unhashable(self):
1115
1199
        """We should get a proper exception here."""
1125
1209
                                         None, ['valid'], ['valid', []])
1126
1210
 
1127
1211
 
1128
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1212
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1129
1213
 
1130
1214
    def setUp(self):
1131
1215
        super(TestPatienceDiffLibFiles, self).setUp()
1132
1216
        self._PatienceSequenceMatcher = \
1133
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1217
            _patiencediff_py.PatienceSequenceMatcher_py
1134
1218
 
1135
1219
    def test_patience_unified_diff_files(self):
1136
1220
        txt_a = ['hello there\n',
1141
1225
        open('a1', 'wb').writelines(txt_a)
1142
1226
        open('b1', 'wb').writelines(txt_b)
1143
1227
 
1144
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1228
        unified_diff_files = patiencediff.unified_diff_files
1145
1229
        psm = self._PatienceSequenceMatcher
1146
 
        self.assertEquals(['--- a1 \n',
1147
 
                           '+++ b1 \n',
 
1230
        self.assertEquals(['--- a1\n',
 
1231
                           '+++ b1\n',
1148
1232
                           '@@ -1,3 +1,2 @@\n',
1149
1233
                           ' hello there\n',
1150
1234
                           '-world\n',
1159
1243
        open('b2', 'wb').writelines(txt_b)
1160
1244
 
1161
1245
        # This is the result with LongestCommonSubstring matching
1162
 
        self.assertEquals(['--- a2 \n',
1163
 
                           '+++ b2 \n',
 
1246
        self.assertEquals(['--- a2\n',
 
1247
                           '+++ b2\n',
1164
1248
                           '@@ -1,6 +1,11 @@\n',
1165
1249
                           ' a\n',
1166
1250
                           ' b\n',
1176
1260
                          , list(unified_diff_files('a2', 'b2')))
1177
1261
 
1178
1262
        # And the patience diff
1179
 
        self.assertEquals(['--- a2 \n',
1180
 
                           '+++ b2 \n',
 
1263
        self.assertEquals(['--- a2\n',
 
1264
                           '+++ b2\n',
1181
1265
                           '@@ -4,6 +4,11 @@\n',
1182
1266
                           ' d\n',
1183
1267
                           ' e\n',
1197
1281
 
1198
1282
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1199
1283
 
1200
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1284
    _test_needs_features = [compiled_patiencediff_feature]
1201
1285
 
1202
1286
    def setUp(self):
1203
1287
        super(TestPatienceDiffLibFiles_c, self).setUp()
1204
 
        import bzrlib._patiencediff_c
 
1288
        from bzrlib import _patiencediff_c
1205
1289
        self._PatienceSequenceMatcher = \
1206
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1207
 
 
1208
 
 
1209
 
class TestUsingCompiledIfAvailable(TestCase):
 
1290
            _patiencediff_c.PatienceSequenceMatcher_c
 
1291
 
 
1292
 
 
1293
class TestUsingCompiledIfAvailable(tests.TestCase):
1210
1294
 
1211
1295
    def test_PatienceSequenceMatcher(self):
1212
 
        if CompiledPatienceDiffFeature.available():
 
1296
        if compiled_patiencediff_feature.available():
1213
1297
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1214
1298
            self.assertIs(PatienceSequenceMatcher_c,
1215
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1299
                          patiencediff.PatienceSequenceMatcher)
1216
1300
        else:
1217
1301
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1218
1302
            self.assertIs(PatienceSequenceMatcher_py,
1219
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1303
                          patiencediff.PatienceSequenceMatcher)
1220
1304
 
1221
1305
    def test_unique_lcs(self):
1222
 
        if CompiledPatienceDiffFeature.available():
 
1306
        if compiled_patiencediff_feature.available():
1223
1307
            from bzrlib._patiencediff_c import unique_lcs_c
1224
1308
            self.assertIs(unique_lcs_c,
1225
 
                          bzrlib.patiencediff.unique_lcs)
 
1309
                          patiencediff.unique_lcs)
1226
1310
        else:
1227
1311
            from bzrlib._patiencediff_py import unique_lcs_py
1228
1312
            self.assertIs(unique_lcs_py,
1229
 
                          bzrlib.patiencediff.unique_lcs)
 
1313
                          patiencediff.unique_lcs)
1230
1314
 
1231
1315
    def test_recurse_matches(self):
1232
 
        if CompiledPatienceDiffFeature.available():
 
1316
        if compiled_patiencediff_feature.available():
1233
1317
            from bzrlib._patiencediff_c import recurse_matches_c
1234
1318
            self.assertIs(recurse_matches_c,
1235
 
                          bzrlib.patiencediff.recurse_matches)
 
1319
                          patiencediff.recurse_matches)
1236
1320
        else:
1237
1321
            from bzrlib._patiencediff_py import recurse_matches_py
1238
1322
            self.assertIs(recurse_matches_py,
1239
 
                          bzrlib.patiencediff.recurse_matches)
1240
 
 
1241
 
 
1242
 
class TestDiffFromTool(TestCaseWithTransport):
 
1323
                          patiencediff.recurse_matches)
 
1324
 
 
1325
 
 
1326
class TestDiffFromTool(tests.TestCaseWithTransport):
1243
1327
 
1244
1328
    def test_from_string(self):
1245
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1329
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1246
1330
        self.addCleanup(diff_obj.finish)
1247
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
 
1331
        self.assertEqual(['diff', '@old_path', '@new_path'],
1248
1332
            diff_obj.command_template)
1249
1333
 
1250
1334
    def test_from_string_u5(self):
1251
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
 
1335
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1336
                                                 None, None, None)
1252
1337
        self.addCleanup(diff_obj.finish)
1253
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
 
1338
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1254
1339
                         diff_obj.command_template)
1255
1340
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1256
1341
                         diff_obj._get_command('old-path', 'new-path'))
1257
1342
 
 
1343
    def test_from_string_path_with_backslashes(self):
 
1344
        self.requireFeature(features.backslashdir_feature)
 
1345
        tool = 'C:\\Tools\\Diff.exe'
 
1346
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
 
1347
        self.addCleanup(diff_obj.finish)
 
1348
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
1349
                         diff_obj.command_template)
 
1350
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
 
1351
                         diff_obj._get_command('old-path', 'new-path'))
 
1352
 
1258
1353
    def test_execute(self):
1259
1354
        output = StringIO()
1260
 
        diff_obj = DiffFromTool(['python', '-c',
1261
 
                                 'print "%(old_path)s %(new_path)s"'],
1262
 
                                None, None, output)
 
1355
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1356
                                      'print "@old_path @new_path"'],
 
1357
                                     None, None, output)
1263
1358
        self.addCleanup(diff_obj.finish)
1264
1359
        diff_obj._execute('old', 'new')
1265
1360
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1266
1361
 
1267
1362
    def test_excute_missing(self):
1268
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1269
 
                                None, None, None)
 
1363
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1364
                                     None, None, None)
1270
1365
        self.addCleanup(diff_obj.finish)
1271
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1272
 
                              'new')
 
1366
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1367
                              'old', 'new')
1273
1368
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1274
1369
                         ' on this machine', str(e))
1275
1370
 
 
1371
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
 
1372
        self.requireFeature(AttribFeature)
 
1373
        output = StringIO()
 
1374
        tree = self.make_branch_and_tree('tree')
 
1375
        self.build_tree_contents([('tree/file', 'content')])
 
1376
        tree.add('file', 'file-id')
 
1377
        tree.commit('old tree')
 
1378
        tree.lock_read()
 
1379
        self.addCleanup(tree.unlock)
 
1380
        basis_tree = tree.basis_tree()
 
1381
        basis_tree.lock_read()
 
1382
        self.addCleanup(basis_tree.unlock)
 
1383
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1384
                                      'print "@old_path @new_path"'],
 
1385
                                     basis_tree, tree, output)
 
1386
        diff_obj._prepare_files('file-id', 'file', 'file')
 
1387
        # The old content should be readonly
 
1388
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
 
1389
                                    r'R.*old\\file$')
 
1390
        # The new content should use the tree object, not a 'new' file anymore
 
1391
        self.assertEndsWith(tree.basedir, 'work/tree')
 
1392
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
 
1393
 
 
1394
    def assertReadableByAttrib(self, cwd, relpath, regex):
 
1395
        proc = subprocess.Popen(['attrib', relpath],
 
1396
                                stdout=subprocess.PIPE,
 
1397
                                cwd=cwd)
 
1398
        (result, err) = proc.communicate()
 
1399
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
 
1400
 
1276
1401
    def test_prepare_files(self):
1277
1402
        output = StringIO()
1278
1403
        tree = self.make_branch_and_tree('tree')
1279
1404
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
 
1405
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1280
1406
        tree.add('oldname', 'file-id')
1281
 
        tree.commit('old tree', timestamp=0)
 
1407
        tree.add('oldname2', 'file2-id')
 
1408
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1409
        tree.commit('old tree', timestamp=315532800)
1282
1410
        tree.rename_one('oldname', 'newname')
 
1411
        tree.rename_one('oldname2', 'newname2')
1283
1412
        self.build_tree_contents([('tree/newname', 'newcontent')])
 
1413
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1284
1414
        old_tree = tree.basis_tree()
1285
1415
        old_tree.lock_read()
1286
1416
        self.addCleanup(old_tree.unlock)
1287
1417
        tree.lock_read()
1288
1418
        self.addCleanup(tree.unlock)
1289
 
        diff_obj = DiffFromTool(['python', '-c',
1290
 
                                 'print "%(old_path)s %(new_path)s"'],
1291
 
                                old_tree, tree, output)
 
1419
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1420
                                      'print "@old_path @new_path"'],
 
1421
                                     old_tree, tree, output)
1292
1422
        self.addCleanup(diff_obj.finish)
1293
1423
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1294
1424
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1295
1425
                                                     'newname')
1296
1426
        self.assertContainsRe(old_path, 'old/oldname$')
1297
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1298
 
        self.assertContainsRe(new_path, 'new/newname$')
 
1427
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
 
1428
        self.assertContainsRe(new_path, 'tree/newname$')
1299
1429
        self.assertFileEqual('oldcontent', old_path)
1300
1430
        self.assertFileEqual('newcontent', new_path)
1301
 
        if osutils.has_symlinks():
 
1431
        if osutils.host_os_dereferences_symlinks():
1302
1432
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1303
1433
        # make sure we can create files with the same parent directories
1304
 
        diff_obj._prepare_files('file-id', 'oldname2', 'newname2')
 
1434
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
 
1435
 
 
1436
 
 
1437
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1438
 
 
1439
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1440
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
 
1441
        TestGetTreesAndBranchesToDiff.
 
1442
        """
 
1443
        return diff.get_trees_and_branches_to_diff_locked(
 
1444
            path_list, revision_specs, old_url, new_url, self.addCleanup)
 
1445
 
 
1446
    def test_basic(self):
 
1447
        tree = self.make_branch_and_tree('tree')
 
1448
        (old_tree, new_tree,
 
1449
         old_branch, new_branch,
 
1450
         specific_files, extra_trees) = self.call_gtabtd(
 
1451
             ['tree'], None, None, None)
 
1452
 
 
1453
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1454
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1455
                         old_tree.get_revision_id())
 
1456
        self.assertEqual(tree.basedir, new_tree.basedir)
 
1457
        self.assertEqual(tree.branch.base, old_branch.base)
 
1458
        self.assertEqual(tree.branch.base, new_branch.base)
 
1459
        self.assertIs(None, specific_files)
 
1460
        self.assertIs(None, extra_trees)
 
1461
 
 
1462
    def test_with_rev_specs(self):
 
1463
        tree = self.make_branch_and_tree('tree')
 
1464
        self.build_tree_contents([('tree/file', 'oldcontent')])
 
1465
        tree.add('file', 'file-id')
 
1466
        tree.commit('old tree', timestamp=0, rev_id="old-id")
 
1467
        self.build_tree_contents([('tree/file', 'newcontent')])
 
1468
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1469
 
 
1470
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1471
                     revisionspec.RevisionSpec.from_string('2')]
 
1472
        (old_tree, new_tree,
 
1473
         old_branch, new_branch,
 
1474
         specific_files, extra_trees) = self.call_gtabtd(
 
1475
            ['tree'], revisions, None, None)
 
1476
 
 
1477
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1478
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1479
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
 
1480
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1481
        self.assertEqual(tree.branch.base, old_branch.base)
 
1482
        self.assertEqual(tree.branch.base, new_branch.base)
 
1483
        self.assertIs(None, specific_files)
 
1484
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
 
1485
 
 
1486
 
 
1487
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
 
1488
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
 
1489
    deprecated get_trees_and_branches_to_diff function.
 
1490
    """
 
1491
 
 
1492
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1493
        return self.applyDeprecated(
 
1494
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
 
1495
            path_list, revision_specs, old_url, new_url)
 
1496