~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 14:01:20 UTC
  • mfrom: (3280.2.5 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080316140120-i3yq8yr1l66m11h7
Start 1.4 development

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 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
from tempfile import TemporaryFile
22
23
 
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,
 
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
34
    )
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):
 
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):
41
44
 
42
45
    def _probe(self):
43
 
        if (sys.platform not in ('cygwin', 'win32')):
44
 
            return False
45
46
        try:
46
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
47
 
        except OSError, e:
 
47
            import bzrlib._patiencediff_c
 
48
        except ImportError:
48
49
            return False
49
 
        return (0 == proc.wait())
 
50
        return True
50
51
 
51
52
    def feature_name(self):
52
 
        return 'attrib Windows command-line tool'
53
 
 
54
 
AttribFeature = _AttribFeature()
55
 
 
56
 
 
57
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
58
 
                                    'bzrlib._patiencediff_c')
59
 
 
 
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
        
60
86
 
61
87
def udiff_lines(old, new, allow_binary=False):
62
88
    output = StringIO()
63
 
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
 
89
    internal_diff('old', old, 'new', new, output, allow_binary)
64
90
    output.seek(0, 0)
65
91
    return output.readlines()
66
92
 
70
96
        # StringIO has no fileno, so it tests a different codepath
71
97
        output = StringIO()
72
98
    else:
73
 
        output = tempfile.TemporaryFile()
 
99
        output = TemporaryFile()
74
100
    try:
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')
 
101
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
102
    except NoDiff:
 
103
        raise TestSkipped('external "diff" not present to test')
78
104
    output.seek(0, 0)
79
105
    lines = output.readlines()
80
106
    output.close()
81
107
    return lines
82
108
 
83
109
 
84
 
class TestDiff(tests.TestCase):
 
110
class TestDiff(TestCase):
85
111
 
86
112
    def test_add_nl(self):
87
113
        """diff generates a valid diff for patches that add a newline"""
123
149
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
150
 
125
151
    def test_binary_lines(self):
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)
 
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)
132
156
 
133
157
    def test_external_diff(self):
134
158
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
164
188
        orig_path = os.environ['PATH']
165
189
        try:
166
190
            os.environ['PATH'] = ''
167
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
 
191
            self.assertRaises(NoDiff, external_diff,
168
192
                              'old', ['boo\n'], 'new', ['goo\n'],
169
193
                              StringIO(), diff_opts=['-u'])
170
194
        finally:
171
195
            os.environ['PATH'] = orig_path
172
 
 
 
196
        
173
197
    def test_internal_diff_default(self):
174
198
        # Default internal diff encoding is utf8
175
199
        output = StringIO()
176
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
177
 
                           u'new_\xe5', ['new_text\n'], output)
 
200
        internal_diff(u'old_\xb5', ['old_text\n'],
 
201
                    u'new_\xe5', ['new_text\n'], output)
178
202
        lines = output.getvalue().splitlines(True)
179
203
        self.check_patch(lines)
180
204
        self.assertEquals(['--- old_\xc2\xb5\n',
188
212
 
189
213
    def test_internal_diff_utf8(self):
190
214
        output = StringIO()
191
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
192
 
                           u'new_\xe5', ['new_text\n'], output,
193
 
                           path_encoding='utf8')
 
215
        internal_diff(u'old_\xb5', ['old_text\n'],
 
216
                    u'new_\xe5', ['new_text\n'], output,
 
217
                    path_encoding='utf8')
194
218
        lines = output.getvalue().splitlines(True)
195
219
        self.check_patch(lines)
196
220
        self.assertEquals(['--- old_\xc2\xb5\n',
204
228
 
205
229
    def test_internal_diff_iso_8859_1(self):
206
230
        output = StringIO()
207
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
208
 
                           u'new_\xe5', ['new_text\n'], output,
209
 
                           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')
210
234
        lines = output.getvalue().splitlines(True)
211
235
        self.check_patch(lines)
212
236
        self.assertEquals(['--- old_\xb5\n',
220
244
 
221
245
    def test_internal_diff_no_content(self):
222
246
        output = StringIO()
223
 
        diff.internal_diff(u'old', [], u'new', [], output)
 
247
        internal_diff(u'old', [], u'new', [], output)
224
248
        self.assertEqual('', output.getvalue())
225
249
 
226
250
    def test_internal_diff_no_changes(self):
227
251
        output = StringIO()
228
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
229
 
                           u'new', ['text\n', 'contents\n'],
230
 
                           output)
 
252
        internal_diff(u'old', ['text\n', 'contents\n'],
 
253
                      u'new', ['text\n', 'contents\n'],
 
254
                      output)
231
255
        self.assertEqual('', output.getvalue())
232
256
 
233
257
    def test_internal_diff_returns_bytes(self):
234
258
        import StringIO
235
259
        output = StringIO.StringIO()
236
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
237
 
                            u'new_\xe5', ['new_text\n'], output)
 
260
        internal_diff(u'old_\xb5', ['old_text\n'],
 
261
                    u'new_\xe5', ['new_text\n'], output)
238
262
        self.failUnless(isinstance(output.getvalue(), str),
239
263
            'internal_diff should return bytestrings')
240
264
 
241
265
 
242
 
class TestDiffFiles(tests.TestCaseInTempDir):
 
266
class TestDiffFiles(TestCaseInTempDir):
243
267
 
244
268
    def test_external_diff_binary(self):
245
269
        """The output when using external diff should use diff's i18n error"""
258
282
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
259
283
 
260
284
 
261
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
 
285
class TestShowDiffTreesHelper(TestCaseWithTransport):
262
286
    """Has a helper for running show_diff_trees"""
263
287
 
264
288
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
267
291
            extra_trees = (working_tree,)
268
292
        else:
269
293
            extra_trees = ()
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/')
 
294
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
 
295
                        extra_trees=extra_trees, old_label='old/',
 
296
                        new_label='new/')
274
297
        return output.getvalue()
275
298
 
276
299
 
341
364
+file2 contents at rev 3
342
365
 
343
366
''')
344
 
 
 
367
        
345
368
    def test_diff_add_files(self):
346
 
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
 
369
        tree1 = self.b.repository.revision_tree(None)
347
370
        tree2 = self.b.repository.revision_tree('rev-1')
348
371
        output = self.get_diff(tree1, tree2)
349
372
        # the files have the epoch time stamp for the tree in which
383
406
        self.wt.rename_one('file1', 'file1b')
384
407
        old_tree = self.b.repository.revision_tree('rev-1')
385
408
        new_tree = self.b.repository.revision_tree('rev-4')
386
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
409
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
387
410
                            working_tree=self.wt)
388
411
        self.assertContainsRe(out, 'file1\t')
389
412
 
395
418
        self.wt.rename_one('file1', 'dir1/file1')
396
419
        old_tree = self.b.repository.revision_tree('rev-1')
397
420
        new_tree = self.b.repository.revision_tree('rev-4')
398
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
421
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
399
422
                            working_tree=self.wt)
400
423
        self.assertContainsRe(out, 'file1\t')
401
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
424
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
402
425
                            working_tree=self.wt)
403
426
        self.assertNotContainsRe(out, 'file1\t')
404
427
 
415
438
        tree.commit('one', rev_id='rev-1')
416
439
 
417
440
        self.build_tree_contents([('tree/file', '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')
 
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')
424
447
 
425
448
    def test_modified_file_in_renamed_dir(self):
426
449
        """Test when a file is modified in a renamed directory."""
432
455
 
433
456
        tree.rename_one('dir', 'other')
434
457
        self.build_tree_contents([('tree/other/file', 'new contents\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")
 
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")
438
461
        # XXX: This is technically incorrect, because it used to be at another
439
462
        # location. What to do?
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')
 
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')
444
467
 
445
468
    def test_renamed_directory(self):
446
469
        """Test when only a directory is only renamed."""
451
474
        tree.commit('one', rev_id='rev-1')
452
475
 
453
476
        tree.rename_one('dir', 'newdir')
454
 
        d = self.get_diff(tree.basis_tree(), tree)
 
477
        diff = self.get_diff(tree.basis_tree(), tree)
455
478
        # Renaming a directory should be a single "you renamed this dir" even
456
479
        # when there are files inside.
457
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
480
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
458
481
 
459
482
    def test_renamed_file(self):
460
483
        """Test when a file is only renamed."""
464
487
        tree.commit('one', rev_id='rev-1')
465
488
 
466
489
        tree.rename_one('file', 'newname')
467
 
        d = self.get_diff(tree.basis_tree(), tree)
468
 
        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")
469
492
        # We shouldn't have a --- or +++ line, because there is no content
470
493
        # change
471
 
        self.assertNotContainsRe(d, '---')
 
494
        self.assertNotContainsRe(diff, '---')
472
495
 
473
496
    def test_renamed_and_modified_file(self):
474
497
        """Test when a file is only renamed."""
479
502
 
480
503
        tree.rename_one('file', 'newname')
481
504
        self.build_tree_contents([('tree/newname', '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'")
 
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')
524
511
 
525
512
    def test_binary_unicode_filenames(self):
526
513
        """Test that contents of files are *not* encoded in UTF-8 when there
527
514
        is a binary file in the diff.
528
515
        """
529
516
        # See https://bugs.launchpad.net/bugs/110092.
530
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
517
        self.requireFeature(UnicodeFilename)
531
518
 
532
519
        # This bug isn't triggered with cStringIO.
533
520
        from StringIO import StringIO
541
528
        tree.add([alpha], ['file-id'])
542
529
        tree.add([omega], ['file-id-2'])
543
530
        diff_content = StringIO()
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,))
 
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,))
552
539
 
553
540
    def test_unicode_filename(self):
554
541
        """Test when the filename are unicode."""
555
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
542
        self.requireFeature(UnicodeFilename)
556
543
 
557
544
        alpha, omega = u'\u03b1', u'\u03c9'
558
545
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
573
560
        tree.add(['add_'+alpha], ['file-id'])
574
561
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
575
562
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
577
 
        self.assertContainsRe(d,
 
563
        diff = self.get_diff(tree.basis_tree(), tree)
 
564
        self.assertContainsRe(diff,
578
565
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
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):
 
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):
628
572
 
629
573
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
630
574
        self.to_file.write('was: ')
634
578
        pass
635
579
 
636
580
 
637
 
class TestDiffTree(tests.TestCaseWithTransport):
 
581
class TestDiffTree(TestCaseWithTransport):
638
582
 
639
583
    def setUp(self):
640
 
        super(TestDiffTree, self).setUp()
 
584
        TestCaseWithTransport.setUp(self)
641
585
        self.old_tree = self.make_branch_and_tree('old-tree')
642
586
        self.old_tree.lock_write()
643
587
        self.addCleanup(self.old_tree.unlock)
644
588
        self.new_tree = self.make_branch_and_tree('new-tree')
645
589
        self.new_tree.lock_write()
646
590
        self.addCleanup(self.new_tree.unlock)
647
 
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
591
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
648
592
 
649
593
    def test_diff_text(self):
650
594
        self.build_tree_contents([('old-tree/olddir/',),
655
599
                                  ('new-tree/newdir/newfile', 'new\n')])
656
600
        self.new_tree.add('newdir')
657
601
        self.new_tree.add('newdir/newfile', 'file-id')
658
 
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
 
602
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
659
603
        differ.diff_text('file-id', None, 'old label', 'new label')
660
604
        self.assertEqual(
661
605
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
690
634
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
691
635
 
692
636
    def test_diff_symlink(self):
693
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
637
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
694
638
        differ.diff_symlink('old target', None)
695
639
        self.assertEqual("=== target was 'old target'\n",
696
640
                         differ.to_file.getvalue())
697
641
 
698
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
642
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
699
643
        differ.diff_symlink(None, 'new target')
700
644
        self.assertEqual("=== target is 'new target'\n",
701
645
                         differ.to_file.getvalue())
702
646
 
703
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
647
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
704
648
        differ.diff_symlink('old target', 'new target')
705
649
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
706
650
                         differ.to_file.getvalue())
736
680
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
737
681
             ' \@\@\n-old\n\n')
738
682
        self.assertContainsRe(self.differ.to_file.getvalue(),
739
 
                              "=== target is u'new'\n")
 
683
                              "=== target is 'new'\n")
740
684
 
741
685
    def test_diff_directory(self):
742
686
        self.build_tree(['new-tree/new-dir/'])
756
700
 
757
701
    def test_register_diff(self):
758
702
        self.create_old_new()
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)
 
703
        old_diff_factories = DiffTree.diff_factories
 
704
        DiffTree.diff_factories=old_diff_factories[:]
 
705
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
762
706
        try:
763
 
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
707
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
764
708
        finally:
765
 
            diff.DiffTree.diff_factories = old_diff_factories
 
709
            DiffTree.diff_factories = old_diff_factories
766
710
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
767
711
        self.assertNotContainsRe(
768
712
            differ.to_file.getvalue(),
773
717
 
774
718
    def test_extra_factories(self):
775
719
        self.create_old_new()
776
 
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
777
 
                               extra_factories=[DiffWasIs.from_diff_tree])
 
720
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
 
721
                            extra_factories=[DiffWasIs.from_diff_tree])
778
722
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
779
723
        self.assertNotContainsRe(
780
724
            differ.to_file.getvalue(),
793
737
            '.*a-file(.|\n)*b-file')
794
738
 
795
739
 
796
 
class TestPatienceDiffLib(tests.TestCase):
 
740
class TestPatienceDiffLib(TestCase):
797
741
 
798
742
    def setUp(self):
799
743
        super(TestPatienceDiffLib, self).setUp()
800
 
        self._unique_lcs = _patiencediff_py.unique_lcs_py
801
 
        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
802
746
        self._PatienceSequenceMatcher = \
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))
 
747
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
811
748
 
812
749
    def test_unique_lcs(self):
813
750
        unique_lcs = self._unique_lcs
819
756
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
820
757
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
821
758
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
822
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
 
759
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
823
760
                                                         (3,3), (4,4)])
824
761
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
825
762
 
840
777
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
841
778
                                          (4, 6), (5, 7), (6, 8)])
842
779
 
843
 
        # recurse_matches doesn't match non-unique
 
780
        # recurse_matches doesn't match non-unique 
844
781
        # lines surrounded by bogus text.
845
782
        # The update has been done in patiencediff.SequenceMatcher instead
846
783
 
983
920
                 ('delete', 1,2, 1,1),
984
921
                 ('equal',  2,3, 1,2),
985
922
                ])
986
 
        chk_ops('aBccDe', 'abccde',
 
923
        chk_ops('aBccDe', 'abccde', 
987
924
                [('equal',   0,1, 0,1),
988
925
                 ('replace', 1,5, 1,5),
989
926
                 ('equal',   5,6, 5,6),
990
927
                ])
991
 
        chk_ops('aBcDec', 'abcdec',
 
928
        chk_ops('aBcDec', 'abcdec', 
992
929
                [('equal',   0,1, 0,1),
993
930
                 ('replace', 1,2, 1,2),
994
931
                 ('equal',   2,3, 2,3),
995
932
                 ('replace', 3,4, 3,4),
996
933
                 ('equal',   4,6, 4,6),
997
934
                ])
998
 
        chk_ops('aBcdEcdFg', 'abcdecdfg',
 
935
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
999
936
                [('equal',   0,1, 0,1),
1000
937
                 ('replace', 1,8, 1,8),
1001
938
                 ('equal',   8,9, 8,9)
1002
939
                ])
1003
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
 
940
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
1004
941
                [('equal',   0,1, 0,1),
1005
942
                 ('replace', 1,2, 1,2),
1006
943
                 ('equal',   2,4, 2,4),
1066
1003
    """
1067
1004
    gnxrf_netf = ['svyr*']
1068
1005
    gnxrf_bcgvbaf = ['ab-erphefr']
1069
 
 
 
1006
  
1070
1007
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1071
1008
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1072
1009
        vs vf_dhvrg():
1080
1017
'''.splitlines(True), '''\
1081
1018
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1082
1019
 
1083
 
    --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 
1084
1021
    nqq gurz.
1085
1022
    """
1086
1023
    gnxrf_netf = ['svyr*']
1113
1050
                 'how are you today?\n']
1114
1051
        txt_b = ['hello there\n',
1115
1052
                 'how are you today?\n']
1116
 
        unified_diff = patiencediff.unified_diff
 
1053
        unified_diff = bzrlib.patiencediff.unified_diff
1117
1054
        psm = self._PatienceSequenceMatcher
1118
 
        self.assertEquals(['--- \n',
1119
 
                           '+++ \n',
 
1055
        self.assertEquals([ '---  \n',
 
1056
                           '+++  \n',
1120
1057
                           '@@ -1,3 +1,2 @@\n',
1121
1058
                           ' hello there\n',
1122
1059
                           '-world\n',
1127
1064
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1128
1065
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1129
1066
        # This is the result with LongestCommonSubstring matching
1130
 
        self.assertEquals(['--- \n',
1131
 
                           '+++ \n',
 
1067
        self.assertEquals(['---  \n',
 
1068
                           '+++  \n',
1132
1069
                           '@@ -1,6 +1,11 @@\n',
1133
1070
                           ' a\n',
1134
1071
                           ' b\n',
1143
1080
                           ' f\n']
1144
1081
                          , list(unified_diff(txt_a, txt_b)))
1145
1082
        # And the patience diff
1146
 
        self.assertEquals(['--- \n',
1147
 
                           '+++ \n',
 
1083
        self.assertEquals(['---  \n',
 
1084
                           '+++  \n',
1148
1085
                           '@@ -4,6 +4,11 @@\n',
1149
1086
                           ' d\n',
1150
1087
                           ' e\n',
1161
1098
                          , list(unified_diff(txt_a, txt_b,
1162
1099
                                 sequencematcher=psm)))
1163
1100
 
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
 
 
1185
1101
 
1186
1102
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1187
1103
 
1188
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1104
    _test_needs_features = [CompiledPatienceDiffFeature]
1189
1105
 
1190
1106
    def setUp(self):
1191
1107
        super(TestPatienceDiffLib_c, self).setUp()
1192
 
        from bzrlib import _patiencediff_c
1193
 
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1194
 
        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
1195
1111
        self._PatienceSequenceMatcher = \
1196
 
            _patiencediff_c.PatienceSequenceMatcher_c
 
1112
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1197
1113
 
1198
1114
    def test_unhashable(self):
1199
1115
        """We should get a proper exception here."""
1209
1125
                                         None, ['valid'], ['valid', []])
1210
1126
 
1211
1127
 
1212
 
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1128
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1213
1129
 
1214
1130
    def setUp(self):
1215
1131
        super(TestPatienceDiffLibFiles, self).setUp()
1216
1132
        self._PatienceSequenceMatcher = \
1217
 
            _patiencediff_py.PatienceSequenceMatcher_py
 
1133
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1218
1134
 
1219
1135
    def test_patience_unified_diff_files(self):
1220
1136
        txt_a = ['hello there\n',
1225
1141
        open('a1', 'wb').writelines(txt_a)
1226
1142
        open('b1', 'wb').writelines(txt_b)
1227
1143
 
1228
 
        unified_diff_files = patiencediff.unified_diff_files
 
1144
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1229
1145
        psm = self._PatienceSequenceMatcher
1230
 
        self.assertEquals(['--- a1\n',
1231
 
                           '+++ b1\n',
 
1146
        self.assertEquals(['--- a1 \n',
 
1147
                           '+++ b1 \n',
1232
1148
                           '@@ -1,3 +1,2 @@\n',
1233
1149
                           ' hello there\n',
1234
1150
                           '-world\n',
1243
1159
        open('b2', 'wb').writelines(txt_b)
1244
1160
 
1245
1161
        # This is the result with LongestCommonSubstring matching
1246
 
        self.assertEquals(['--- a2\n',
1247
 
                           '+++ b2\n',
 
1162
        self.assertEquals(['--- a2 \n',
 
1163
                           '+++ b2 \n',
1248
1164
                           '@@ -1,6 +1,11 @@\n',
1249
1165
                           ' a\n',
1250
1166
                           ' b\n',
1260
1176
                          , list(unified_diff_files('a2', 'b2')))
1261
1177
 
1262
1178
        # And the patience diff
1263
 
        self.assertEquals(['--- a2\n',
1264
 
                           '+++ b2\n',
 
1179
        self.assertEquals(['--- a2 \n',
 
1180
                           '+++ b2 \n',
1265
1181
                           '@@ -4,6 +4,11 @@\n',
1266
1182
                           ' d\n',
1267
1183
                           ' e\n',
1281
1197
 
1282
1198
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1283
1199
 
1284
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1200
    _test_needs_features = [CompiledPatienceDiffFeature]
1285
1201
 
1286
1202
    def setUp(self):
1287
1203
        super(TestPatienceDiffLibFiles_c, self).setUp()
1288
 
        from bzrlib import _patiencediff_c
 
1204
        import bzrlib._patiencediff_c
1289
1205
        self._PatienceSequenceMatcher = \
1290
 
            _patiencediff_c.PatienceSequenceMatcher_c
1291
 
 
1292
 
 
1293
 
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1206
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1207
 
 
1208
 
 
1209
class TestUsingCompiledIfAvailable(TestCase):
1294
1210
 
1295
1211
    def test_PatienceSequenceMatcher(self):
1296
 
        if compiled_patiencediff_feature.available():
 
1212
        if CompiledPatienceDiffFeature.available():
1297
1213
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1298
1214
            self.assertIs(PatienceSequenceMatcher_c,
1299
 
                          patiencediff.PatienceSequenceMatcher)
 
1215
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1300
1216
        else:
1301
1217
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1302
1218
            self.assertIs(PatienceSequenceMatcher_py,
1303
 
                          patiencediff.PatienceSequenceMatcher)
 
1219
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1304
1220
 
1305
1221
    def test_unique_lcs(self):
1306
 
        if compiled_patiencediff_feature.available():
 
1222
        if CompiledPatienceDiffFeature.available():
1307
1223
            from bzrlib._patiencediff_c import unique_lcs_c
1308
1224
            self.assertIs(unique_lcs_c,
1309
 
                          patiencediff.unique_lcs)
 
1225
                          bzrlib.patiencediff.unique_lcs)
1310
1226
        else:
1311
1227
            from bzrlib._patiencediff_py import unique_lcs_py
1312
1228
            self.assertIs(unique_lcs_py,
1313
 
                          patiencediff.unique_lcs)
 
1229
                          bzrlib.patiencediff.unique_lcs)
1314
1230
 
1315
1231
    def test_recurse_matches(self):
1316
 
        if compiled_patiencediff_feature.available():
 
1232
        if CompiledPatienceDiffFeature.available():
1317
1233
            from bzrlib._patiencediff_c import recurse_matches_c
1318
1234
            self.assertIs(recurse_matches_c,
1319
 
                          patiencediff.recurse_matches)
 
1235
                          bzrlib.patiencediff.recurse_matches)
1320
1236
        else:
1321
1237
            from bzrlib._patiencediff_py import recurse_matches_py
1322
1238
            self.assertIs(recurse_matches_py,
1323
 
                          patiencediff.recurse_matches)
1324
 
 
1325
 
 
1326
 
class TestDiffFromTool(tests.TestCaseWithTransport):
 
1239
                          bzrlib.patiencediff.recurse_matches)
 
1240
 
 
1241
 
 
1242
class TestDiffFromTool(TestCaseWithTransport):
1327
1243
 
1328
1244
    def test_from_string(self):
1329
 
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
 
1245
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1330
1246
        self.addCleanup(diff_obj.finish)
1331
 
        self.assertEqual(['diff', '@old_path', '@new_path'],
 
1247
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1332
1248
            diff_obj.command_template)
1333
1249
 
1334
1250
    def test_from_string_u5(self):
1335
 
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1336
 
                                                 None, None, None)
 
1251
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1337
1252
        self.addCleanup(diff_obj.finish)
1338
 
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
 
1253
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1339
1254
                         diff_obj.command_template)
1340
1255
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1341
1256
                         diff_obj._get_command('old-path', 'new-path'))
1342
1257
 
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
 
 
1353
1258
    def test_execute(self):
1354
1259
        output = StringIO()
1355
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1356
 
                                      'print "@old_path @new_path"'],
1357
 
                                     None, None, output)
 
1260
        diff_obj = DiffFromTool(['python', '-c',
 
1261
                                 'print "%(old_path)s %(new_path)s"'],
 
1262
                                None, None, output)
1358
1263
        self.addCleanup(diff_obj.finish)
1359
1264
        diff_obj._execute('old', 'new')
1360
1265
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1361
1266
 
1362
1267
    def test_excute_missing(self):
1363
 
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1364
 
                                     None, None, None)
 
1268
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1269
                                None, None, None)
1365
1270
        self.addCleanup(diff_obj.finish)
1366
 
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1367
 
                              'old', 'new')
 
1271
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
 
1272
                              'new')
1368
1273
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1369
1274
                         ' on this machine', str(e))
1370
1275
 
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
 
 
1401
1276
    def test_prepare_files(self):
1402
1277
        output = StringIO()
1403
1278
        tree = self.make_branch_and_tree('tree')
1404
1279
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1405
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1406
1280
        tree.add('oldname', 'file-id')
1407
 
        tree.add('oldname2', 'file2-id')
1408
 
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1409
 
        tree.commit('old tree', timestamp=315532800)
 
1281
        tree.commit('old tree', timestamp=0)
1410
1282
        tree.rename_one('oldname', 'newname')
1411
 
        tree.rename_one('oldname2', 'newname2')
1412
1283
        self.build_tree_contents([('tree/newname', 'newcontent')])
1413
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1414
1284
        old_tree = tree.basis_tree()
1415
1285
        old_tree.lock_read()
1416
1286
        self.addCleanup(old_tree.unlock)
1417
1287
        tree.lock_read()
1418
1288
        self.addCleanup(tree.unlock)
1419
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1420
 
                                      'print "@old_path @new_path"'],
1421
 
                                     old_tree, tree, output)
 
1289
        diff_obj = DiffFromTool(['python', '-c',
 
1290
                                 'print "%(old_path)s %(new_path)s"'],
 
1291
                                old_tree, tree, output)
1422
1292
        self.addCleanup(diff_obj.finish)
1423
1293
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1424
1294
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1425
1295
                                                     'newname')
1426
1296
        self.assertContainsRe(old_path, 'old/oldname$')
1427
 
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1428
 
        self.assertContainsRe(new_path, 'tree/newname$')
 
1297
        self.assertEqual(0, os.stat(old_path).st_mtime)
 
1298
        self.assertContainsRe(new_path, 'new/newname$')
1429
1299
        self.assertFileEqual('oldcontent', old_path)
1430
1300
        self.assertFileEqual('newcontent', new_path)
1431
 
        if osutils.host_os_dereferences_symlinks():
 
1301
        if osutils.has_symlinks():
1432
1302
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1433
1303
        # make sure we can create files with the same parent directories
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
 
 
 
1304
        diff_obj._prepare_files('file-id', 'oldname2', 'newname2')