~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Martin
  • Date: 2010-05-16 15:18:43 UTC
  • mfrom: (5235 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5239.
  • Revision ID: gzlist@googlemail.com-20100516151843-lu53u7caehm3ie3i
Merge bzr.dev to resolve conflicts in NEWS and _chk_map_pyx

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
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import os.path
19
18
from cStringIO import StringIO
20
 
import errno
21
19
import subprocess
22
20
import sys
23
 
from tempfile import TemporaryFile
 
21
import tempfile
24
22
 
25
 
from bzrlib import tests
26
 
from bzrlib.diff import (
27
 
    DiffFromTool,
28
 
    DiffPath,
29
 
    DiffSymlink,
30
 
    DiffTree,
31
 
    DiffText,
32
 
    external_diff,
33
 
    internal_diff,
34
 
    show_diff_trees,
 
23
from bzrlib import (
 
24
    diff,
 
25
    errors,
 
26
    osutils,
 
27
    patiencediff,
 
28
    _patiencediff_py,
 
29
    revision as _mod_revision,
 
30
    revisionspec,
 
31
    revisiontree,
 
32
    tests,
 
33
    transform,
35
34
    )
36
 
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
37
 
import bzrlib.osutils as osutils
38
 
import bzrlib.revision as _mod_revision
39
 
import bzrlib.transform as transform
40
 
import bzrlib.patiencediff
41
 
import bzrlib._patiencediff_py
42
 
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
43
 
                          TestCaseInTempDir, TestSkipped)
44
 
 
45
 
 
46
 
class _AttribFeature(Feature):
 
35
from bzrlib.symbol_versioning import deprecated_in
 
36
from bzrlib.tests import test_win32utils
 
37
 
 
38
 
 
39
class _AttribFeature(tests.Feature):
47
40
 
48
41
    def _probe(self):
49
42
        if (sys.platform not in ('cygwin', 'win32')):
60
53
AttribFeature = _AttribFeature()
61
54
 
62
55
 
63
 
class _CompiledPatienceDiffFeature(Feature):
64
 
 
65
 
    def _probe(self):
66
 
        try:
67
 
            import bzrlib._patiencediff_c
68
 
        except ImportError:
69
 
            return False
70
 
        return True
71
 
 
72
 
    def feature_name(self):
73
 
        return 'bzrlib._patiencediff_c'
74
 
 
75
 
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
56
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
 
57
                                    'bzrlib._patiencediff_c')
76
58
 
77
59
 
78
60
def udiff_lines(old, new, allow_binary=False):
79
61
    output = StringIO()
80
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
62
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
81
63
    output.seek(0, 0)
82
64
    return output.readlines()
83
65
 
87
69
        # StringIO has no fileno, so it tests a different codepath
88
70
        output = StringIO()
89
71
    else:
90
 
        output = TemporaryFile()
 
72
        output = tempfile.TemporaryFile()
91
73
    try:
92
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
93
 
    except NoDiff:
94
 
        raise TestSkipped('external "diff" not present to test')
 
74
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
75
    except errors.NoDiff:
 
76
        raise tests.TestSkipped('external "diff" not present to test')
95
77
    output.seek(0, 0)
96
78
    lines = output.readlines()
97
79
    output.close()
98
80
    return lines
99
81
 
100
82
 
101
 
class TestDiff(TestCase):
 
83
class TestDiff(tests.TestCase):
102
84
 
103
85
    def test_add_nl(self):
104
86
        """diff generates a valid diff for patches that add a newline"""
140
122
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
141
123
 
142
124
    def test_binary_lines(self):
143
 
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
144
 
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
145
 
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
146
 
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
125
        empty = []
 
126
        uni_lines = [1023 * 'a' + '\x00']
 
127
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
 
128
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
 
129
        udiff_lines(uni_lines , empty, allow_binary=True)
 
130
        udiff_lines(empty, uni_lines, allow_binary=True)
147
131
 
148
132
    def test_external_diff(self):
149
133
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
179
163
        orig_path = os.environ['PATH']
180
164
        try:
181
165
            os.environ['PATH'] = ''
182
 
            self.assertRaises(NoDiff, external_diff,
 
166
            self.assertRaises(errors.NoDiff, diff.external_diff,
183
167
                              'old', ['boo\n'], 'new', ['goo\n'],
184
168
                              StringIO(), diff_opts=['-u'])
185
169
        finally:
188
172
    def test_internal_diff_default(self):
189
173
        # Default internal diff encoding is utf8
190
174
        output = StringIO()
191
 
        internal_diff(u'old_\xb5', ['old_text\n'],
192
 
                    u'new_\xe5', ['new_text\n'], output)
 
175
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
176
                           u'new_\xe5', ['new_text\n'], output)
193
177
        lines = output.getvalue().splitlines(True)
194
178
        self.check_patch(lines)
195
179
        self.assertEquals(['--- old_\xc2\xb5\n',
203
187
 
204
188
    def test_internal_diff_utf8(self):
205
189
        output = StringIO()
206
 
        internal_diff(u'old_\xb5', ['old_text\n'],
207
 
                    u'new_\xe5', ['new_text\n'], output,
208
 
                    path_encoding='utf8')
 
190
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
191
                           u'new_\xe5', ['new_text\n'], output,
 
192
                           path_encoding='utf8')
209
193
        lines = output.getvalue().splitlines(True)
210
194
        self.check_patch(lines)
211
195
        self.assertEquals(['--- old_\xc2\xb5\n',
219
203
 
220
204
    def test_internal_diff_iso_8859_1(self):
221
205
        output = StringIO()
222
 
        internal_diff(u'old_\xb5', ['old_text\n'],
223
 
                    u'new_\xe5', ['new_text\n'], output,
224
 
                    path_encoding='iso-8859-1')
 
206
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
207
                           u'new_\xe5', ['new_text\n'], output,
 
208
                           path_encoding='iso-8859-1')
225
209
        lines = output.getvalue().splitlines(True)
226
210
        self.check_patch(lines)
227
211
        self.assertEquals(['--- old_\xb5\n',
235
219
 
236
220
    def test_internal_diff_no_content(self):
237
221
        output = StringIO()
238
 
        internal_diff(u'old', [], u'new', [], output)
 
222
        diff.internal_diff(u'old', [], u'new', [], output)
239
223
        self.assertEqual('', output.getvalue())
240
224
 
241
225
    def test_internal_diff_no_changes(self):
242
226
        output = StringIO()
243
 
        internal_diff(u'old', ['text\n', 'contents\n'],
244
 
                      u'new', ['text\n', 'contents\n'],
245
 
                      output)
 
227
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
228
                           u'new', ['text\n', 'contents\n'],
 
229
                           output)
246
230
        self.assertEqual('', output.getvalue())
247
231
 
248
232
    def test_internal_diff_returns_bytes(self):
249
233
        import StringIO
250
234
        output = StringIO.StringIO()
251
 
        internal_diff(u'old_\xb5', ['old_text\n'],
252
 
                    u'new_\xe5', ['new_text\n'], output)
 
235
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
236
                            u'new_\xe5', ['new_text\n'], output)
253
237
        self.failUnless(isinstance(output.getvalue(), str),
254
238
            'internal_diff should return bytestrings')
255
239
 
256
240
 
257
 
class TestDiffFiles(TestCaseInTempDir):
 
241
class TestDiffFiles(tests.TestCaseInTempDir):
258
242
 
259
243
    def test_external_diff_binary(self):
260
244
        """The output when using external diff should use diff's i18n error"""
273
257
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
274
258
 
275
259
 
276
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
 
260
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
277
261
    """Has a helper for running show_diff_trees"""
278
262
 
279
263
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
282
266
            extra_trees = (working_tree,)
283
267
        else:
284
268
            extra_trees = ()
285
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
286
 
                        extra_trees=extra_trees, old_label='old/',
287
 
                        new_label='new/')
 
269
        diff.show_diff_trees(tree1, tree2, output,
 
270
                             specific_files=specific_files,
 
271
                             extra_trees=extra_trees, old_label='old/',
 
272
                             new_label='new/')
288
273
        return output.getvalue()
289
274
 
290
275
 
429
414
        tree.commit('one', rev_id='rev-1')
430
415
 
431
416
        self.build_tree_contents([('tree/file', 'new contents\n')])
432
 
        diff = self.get_diff(tree.basis_tree(), tree)
433
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
434
 
        self.assertContainsRe(diff, '--- old/file\t')
435
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
436
 
        self.assertContainsRe(diff, '-contents\n'
437
 
                                    '\\+new contents\n')
 
417
        d = self.get_diff(tree.basis_tree(), tree)
 
418
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
419
        self.assertContainsRe(d, '--- old/file\t')
 
420
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
421
        self.assertContainsRe(d, '-contents\n'
 
422
                                 '\\+new contents\n')
438
423
 
439
424
    def test_modified_file_in_renamed_dir(self):
440
425
        """Test when a file is modified in a renamed directory."""
446
431
 
447
432
        tree.rename_one('dir', 'other')
448
433
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
449
 
        diff = self.get_diff(tree.basis_tree(), tree)
450
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
451
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
 
434
        d = self.get_diff(tree.basis_tree(), tree)
 
435
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
436
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
452
437
        # XXX: This is technically incorrect, because it used to be at another
453
438
        # location. What to do?
454
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
455
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
456
 
        self.assertContainsRe(diff, '-contents\n'
457
 
                                    '\\+new contents\n')
 
439
        self.assertContainsRe(d, '--- old/dir/file\t')
 
440
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
441
        self.assertContainsRe(d, '-contents\n'
 
442
                                 '\\+new contents\n')
458
443
 
459
444
    def test_renamed_directory(self):
460
445
        """Test when only a directory is only renamed."""
465
450
        tree.commit('one', rev_id='rev-1')
466
451
 
467
452
        tree.rename_one('dir', 'newdir')
468
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
453
        d = self.get_diff(tree.basis_tree(), tree)
469
454
        # Renaming a directory should be a single "you renamed this dir" even
470
455
        # when there are files inside.
471
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
456
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
472
457
 
473
458
    def test_renamed_file(self):
474
459
        """Test when a file is only renamed."""
478
463
        tree.commit('one', rev_id='rev-1')
479
464
 
480
465
        tree.rename_one('file', 'newname')
481
 
        diff = self.get_diff(tree.basis_tree(), tree)
482
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
466
        d = self.get_diff(tree.basis_tree(), tree)
 
467
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
468
        # We shouldn't have a --- or +++ line, because there is no content
484
469
        # change
485
 
        self.assertNotContainsRe(diff, '---')
 
470
        self.assertNotContainsRe(d, '---')
486
471
 
487
472
    def test_renamed_and_modified_file(self):
488
473
        """Test when a file is only renamed."""
493
478
 
494
479
        tree.rename_one('file', 'newname')
495
480
        self.build_tree_contents([('tree/newname', 'new contents\n')])
496
 
        diff = self.get_diff(tree.basis_tree(), tree)
497
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
498
 
        self.assertContainsRe(diff, '--- old/file\t')
499
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
500
 
        self.assertContainsRe(diff, '-contents\n'
501
 
                                    '\\+new contents\n')
 
481
        d = self.get_diff(tree.basis_tree(), tree)
 
482
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
483
        self.assertContainsRe(d, '--- old/file\t')
 
484
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
485
        self.assertContainsRe(d, '-contents\n'
 
486
                                 '\\+new contents\n')
502
487
 
503
488
 
504
489
    def test_internal_diff_exec_property(self):
523
508
        tree.rename_one('c', 'new-c')
524
509
        tree.rename_one('d', 'new-d')
525
510
 
526
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
511
        d = self.get_diff(tree.basis_tree(), tree)
527
512
 
528
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
529
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
530
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
531
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
532
 
        self.assertNotContainsRe(diff, r"file 'e'")
533
 
        self.assertNotContainsRe(diff, r"file 'f'")
 
513
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
514
                                  ".*\+x to -x.*\)")
 
515
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
516
                                  ".*-x to \+x.*\)")
 
517
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
518
                                  ".*\+x to -x.*\)")
 
519
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
520
                                  ".*-x to \+x.*\)")
 
521
        self.assertNotContainsRe(d, r"file 'e'")
 
522
        self.assertNotContainsRe(d, r"file 'f'")
534
523
 
535
524
 
536
525
    def test_binary_unicode_filenames(self):
552
541
        tree.add([alpha], ['file-id'])
553
542
        tree.add([omega], ['file-id-2'])
554
543
        diff_content = StringIO()
555
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
556
 
        diff = diff_content.getvalue()
557
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
558
 
        self.assertContainsRe(
559
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
560
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
561
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
562
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
 
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,))
563
552
 
564
553
    def test_unicode_filename(self):
565
554
        """Test when the filename are unicode."""
584
573
        tree.add(['add_'+alpha], ['file-id'])
585
574
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
586
575
 
587
 
        diff = self.get_diff(tree.basis_tree(), tree)
588
 
        self.assertContainsRe(diff,
 
576
        d = self.get_diff(tree.basis_tree(), tree)
 
577
        self.assertContainsRe(d,
589
578
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
590
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
591
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
592
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
593
 
 
594
 
 
595
 
class DiffWasIs(DiffPath):
 
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
 
 
584
class DiffWasIs(diff.DiffPath):
596
585
 
597
586
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
598
587
        self.to_file.write('was: ')
602
591
        pass
603
592
 
604
593
 
605
 
class TestDiffTree(TestCaseWithTransport):
 
594
class TestDiffTree(tests.TestCaseWithTransport):
606
595
 
607
596
    def setUp(self):
608
 
        TestCaseWithTransport.setUp(self)
 
597
        super(TestDiffTree, self).setUp()
609
598
        self.old_tree = self.make_branch_and_tree('old-tree')
610
599
        self.old_tree.lock_write()
611
600
        self.addCleanup(self.old_tree.unlock)
612
601
        self.new_tree = self.make_branch_and_tree('new-tree')
613
602
        self.new_tree.lock_write()
614
603
        self.addCleanup(self.new_tree.unlock)
615
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
604
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
616
605
 
617
606
    def test_diff_text(self):
618
607
        self.build_tree_contents([('old-tree/olddir/',),
623
612
                                  ('new-tree/newdir/newfile', 'new\n')])
624
613
        self.new_tree.add('newdir')
625
614
        self.new_tree.add('newdir/newfile', 'file-id')
626
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
615
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
627
616
        differ.diff_text('file-id', None, 'old label', 'new label')
628
617
        self.assertEqual(
629
618
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
658
647
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
659
648
 
660
649
    def test_diff_symlink(self):
661
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
650
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
662
651
        differ.diff_symlink('old target', None)
663
652
        self.assertEqual("=== target was 'old target'\n",
664
653
                         differ.to_file.getvalue())
665
654
 
666
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
655
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
667
656
        differ.diff_symlink(None, 'new target')
668
657
        self.assertEqual("=== target is 'new target'\n",
669
658
                         differ.to_file.getvalue())
670
659
 
671
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
660
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
672
661
        differ.diff_symlink('old target', 'new target')
673
662
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
674
663
                         differ.to_file.getvalue())
704
693
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
705
694
             ' \@\@\n-old\n\n')
706
695
        self.assertContainsRe(self.differ.to_file.getvalue(),
707
 
                              "=== target is 'new'\n")
 
696
                              "=== target is u'new'\n")
708
697
 
709
698
    def test_diff_directory(self):
710
699
        self.build_tree(['new-tree/new-dir/'])
724
713
 
725
714
    def test_register_diff(self):
726
715
        self.create_old_new()
727
 
        old_diff_factories = DiffTree.diff_factories
728
 
        DiffTree.diff_factories=old_diff_factories[:]
729
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
716
        old_diff_factories = diff.DiffTree.diff_factories
 
717
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
718
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
730
719
        try:
731
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
720
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
732
721
        finally:
733
 
            DiffTree.diff_factories = old_diff_factories
 
722
            diff.DiffTree.diff_factories = old_diff_factories
734
723
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
735
724
        self.assertNotContainsRe(
736
725
            differ.to_file.getvalue(),
741
730
 
742
731
    def test_extra_factories(self):
743
732
        self.create_old_new()
744
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
745
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
733
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
734
                               extra_factories=[DiffWasIs.from_diff_tree])
746
735
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
747
736
        self.assertNotContainsRe(
748
737
            differ.to_file.getvalue(),
761
750
            '.*a-file(.|\n)*b-file')
762
751
 
763
752
 
764
 
class TestPatienceDiffLib(TestCase):
 
753
class TestPatienceDiffLib(tests.TestCase):
765
754
 
766
755
    def setUp(self):
767
756
        super(TestPatienceDiffLib, self).setUp()
768
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
769
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
 
757
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
758
        self._recurse_matches = _patiencediff_py.recurse_matches_py
770
759
        self._PatienceSequenceMatcher = \
771
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
760
            _patiencediff_py.PatienceSequenceMatcher_py
772
761
 
773
762
    def test_diff_unicode_string(self):
774
763
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
1081
1070
                 'how are you today?\n']
1082
1071
        txt_b = ['hello there\n',
1083
1072
                 'how are you today?\n']
1084
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1073
        unified_diff = patiencediff.unified_diff
1085
1074
        psm = self._PatienceSequenceMatcher
1086
1075
        self.assertEquals(['--- \n',
1087
1076
                           '+++ \n',
1135
1124
                 'how are you today?\n']
1136
1125
        txt_b = ['hello there\n',
1137
1126
                 'how are you today?\n']
1138
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1127
        unified_diff = patiencediff.unified_diff
1139
1128
        psm = self._PatienceSequenceMatcher
1140
1129
        self.assertEquals(['--- a\t2008-08-08\n',
1141
1130
                           '+++ b\t2008-09-09\n',
1153
1142
 
1154
1143
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1155
1144
 
1156
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1145
    _test_needs_features = [compiled_patiencediff_feature]
1157
1146
 
1158
1147
    def setUp(self):
1159
1148
        super(TestPatienceDiffLib_c, self).setUp()
1160
 
        import bzrlib._patiencediff_c
1161
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1162
 
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
 
1149
        from bzrlib import _patiencediff_c
 
1150
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1151
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1163
1152
        self._PatienceSequenceMatcher = \
1164
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1153
            _patiencediff_c.PatienceSequenceMatcher_c
1165
1154
 
1166
1155
    def test_unhashable(self):
1167
1156
        """We should get a proper exception here."""
1177
1166
                                         None, ['valid'], ['valid', []])
1178
1167
 
1179
1168
 
1180
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1169
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1181
1170
 
1182
1171
    def setUp(self):
1183
1172
        super(TestPatienceDiffLibFiles, self).setUp()
1184
1173
        self._PatienceSequenceMatcher = \
1185
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1174
            _patiencediff_py.PatienceSequenceMatcher_py
1186
1175
 
1187
1176
    def test_patience_unified_diff_files(self):
1188
1177
        txt_a = ['hello there\n',
1193
1182
        open('a1', 'wb').writelines(txt_a)
1194
1183
        open('b1', 'wb').writelines(txt_b)
1195
1184
 
1196
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1185
        unified_diff_files = patiencediff.unified_diff_files
1197
1186
        psm = self._PatienceSequenceMatcher
1198
1187
        self.assertEquals(['--- a1\n',
1199
1188
                           '+++ b1\n',
1249
1238
 
1250
1239
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1251
1240
 
1252
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1241
    _test_needs_features = [compiled_patiencediff_feature]
1253
1242
 
1254
1243
    def setUp(self):
1255
1244
        super(TestPatienceDiffLibFiles_c, self).setUp()
1256
 
        import bzrlib._patiencediff_c
 
1245
        from bzrlib import _patiencediff_c
1257
1246
        self._PatienceSequenceMatcher = \
1258
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1259
 
 
1260
 
 
1261
 
class TestUsingCompiledIfAvailable(TestCase):
 
1247
            _patiencediff_c.PatienceSequenceMatcher_c
 
1248
 
 
1249
 
 
1250
class TestUsingCompiledIfAvailable(tests.TestCase):
1262
1251
 
1263
1252
    def test_PatienceSequenceMatcher(self):
1264
 
        if CompiledPatienceDiffFeature.available():
 
1253
        if compiled_patiencediff_feature.available():
1265
1254
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1266
1255
            self.assertIs(PatienceSequenceMatcher_c,
1267
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1256
                          patiencediff.PatienceSequenceMatcher)
1268
1257
        else:
1269
1258
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1270
1259
            self.assertIs(PatienceSequenceMatcher_py,
1271
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1260
                          patiencediff.PatienceSequenceMatcher)
1272
1261
 
1273
1262
    def test_unique_lcs(self):
1274
 
        if CompiledPatienceDiffFeature.available():
 
1263
        if compiled_patiencediff_feature.available():
1275
1264
            from bzrlib._patiencediff_c import unique_lcs_c
1276
1265
            self.assertIs(unique_lcs_c,
1277
 
                          bzrlib.patiencediff.unique_lcs)
 
1266
                          patiencediff.unique_lcs)
1278
1267
        else:
1279
1268
            from bzrlib._patiencediff_py import unique_lcs_py
1280
1269
            self.assertIs(unique_lcs_py,
1281
 
                          bzrlib.patiencediff.unique_lcs)
 
1270
                          patiencediff.unique_lcs)
1282
1271
 
1283
1272
    def test_recurse_matches(self):
1284
 
        if CompiledPatienceDiffFeature.available():
 
1273
        if compiled_patiencediff_feature.available():
1285
1274
            from bzrlib._patiencediff_c import recurse_matches_c
1286
1275
            self.assertIs(recurse_matches_c,
1287
 
                          bzrlib.patiencediff.recurse_matches)
 
1276
                          patiencediff.recurse_matches)
1288
1277
        else:
1289
1278
            from bzrlib._patiencediff_py import recurse_matches_py
1290
1279
            self.assertIs(recurse_matches_py,
1291
 
                          bzrlib.patiencediff.recurse_matches)
1292
 
 
1293
 
 
1294
 
class TestDiffFromTool(TestCaseWithTransport):
 
1280
                          patiencediff.recurse_matches)
 
1281
 
 
1282
 
 
1283
class TestDiffFromTool(tests.TestCaseWithTransport):
1295
1284
 
1296
1285
    def test_from_string(self):
1297
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1286
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1298
1287
        self.addCleanup(diff_obj.finish)
1299
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
 
1288
        self.assertEqual(['diff', '@old_path', '@new_path'],
1300
1289
            diff_obj.command_template)
1301
1290
 
1302
1291
    def test_from_string_u5(self):
1303
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
 
1292
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1293
                                                 None, None, None)
1304
1294
        self.addCleanup(diff_obj.finish)
1305
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
 
1295
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1306
1296
                         diff_obj.command_template)
1307
1297
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1308
1298
                         diff_obj._get_command('old-path', 'new-path'))
1309
1299
 
 
1300
    def test_from_string_path_with_backslashes(self):
 
1301
        self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
 
1302
        tool = 'C:\\Tools\\Diff.exe'
 
1303
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
 
1304
        self.addCleanup(diff_obj.finish)
 
1305
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
1306
                         diff_obj.command_template)
 
1307
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
 
1308
                         diff_obj._get_command('old-path', 'new-path'))
 
1309
 
1310
1310
    def test_execute(self):
1311
1311
        output = StringIO()
1312
 
        diff_obj = DiffFromTool(['python', '-c',
1313
 
                                 'print "%(old_path)s %(new_path)s"'],
1314
 
                                None, None, output)
 
1312
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1313
                                      'print "@old_path @new_path"'],
 
1314
                                     None, None, output)
1315
1315
        self.addCleanup(diff_obj.finish)
1316
1316
        diff_obj._execute('old', 'new')
1317
1317
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1318
1318
 
1319
1319
    def test_excute_missing(self):
1320
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
 
                                None, None, None)
 
1320
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1321
                                     None, None, None)
1322
1322
        self.addCleanup(diff_obj.finish)
1323
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1324
 
                              'new')
 
1323
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1324
                              'old', 'new')
1325
1325
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1326
1326
                         ' on this machine', str(e))
1327
1327
 
1334
1334
        tree.commit('old tree')
1335
1335
        tree.lock_read()
1336
1336
        self.addCleanup(tree.unlock)
1337
 
        diff_obj = DiffFromTool(['python', '-c',
1338
 
                                 'print "%(old_path)s %(new_path)s"'],
1339
 
                                tree, tree, output)
 
1337
        basis_tree = tree.basis_tree()
 
1338
        basis_tree.lock_read()
 
1339
        self.addCleanup(basis_tree.unlock)
 
1340
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1341
                                      'print "@old_path @new_path"'],
 
1342
                                     basis_tree, tree, output)
1340
1343
        diff_obj._prepare_files('file-id', 'file', 'file')
1341
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1342
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
 
1344
        # The old content should be readonly
 
1345
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
 
1346
                                    r'R.*old\\file$')
 
1347
        # The new content should use the tree object, not a 'new' file anymore
 
1348
        self.assertEndsWith(tree.basedir, 'work/tree')
 
1349
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1343
1350
 
1344
1351
    def assertReadableByAttrib(self, cwd, relpath, regex):
1345
1352
        proc = subprocess.Popen(['attrib', relpath],
1346
1353
                                stdout=subprocess.PIPE,
1347
1354
                                cwd=cwd)
1348
 
        proc.wait()
1349
 
        result = proc.stdout.read()
1350
 
        self.assertContainsRe(result, regex)
 
1355
        (result, err) = proc.communicate()
 
1356
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1351
1357
 
1352
1358
    def test_prepare_files(self):
1353
1359
        output = StringIO()
1356
1362
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1357
1363
        tree.add('oldname', 'file-id')
1358
1364
        tree.add('oldname2', 'file2-id')
1359
 
        tree.commit('old tree', timestamp=0)
 
1365
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1366
        tree.commit('old tree', timestamp=315532800)
1360
1367
        tree.rename_one('oldname', 'newname')
1361
1368
        tree.rename_one('oldname2', 'newname2')
1362
1369
        self.build_tree_contents([('tree/newname', 'newcontent')])
1366
1373
        self.addCleanup(old_tree.unlock)
1367
1374
        tree.lock_read()
1368
1375
        self.addCleanup(tree.unlock)
1369
 
        diff_obj = DiffFromTool(['python', '-c',
1370
 
                                 'print "%(old_path)s %(new_path)s"'],
1371
 
                                old_tree, tree, output)
 
1376
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1377
                                      'print "@old_path @new_path"'],
 
1378
                                     old_tree, tree, output)
1372
1379
        self.addCleanup(diff_obj.finish)
1373
1380
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1374
1381
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1375
1382
                                                     'newname')
1376
1383
        self.assertContainsRe(old_path, 'old/oldname$')
1377
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1378
 
        self.assertContainsRe(new_path, 'new/newname$')
 
1384
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
 
1385
        self.assertContainsRe(new_path, 'tree/newname$')
1379
1386
        self.assertFileEqual('oldcontent', old_path)
1380
1387
        self.assertFileEqual('newcontent', new_path)
1381
1388
        if osutils.host_os_dereferences_symlinks():
1382
1389
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1383
1390
        # make sure we can create files with the same parent directories
1384
1391
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
 
1392
 
 
1393
 
 
1394
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1395
 
 
1396
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1397
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
 
1398
        TestGetTreesAndBranchesToDiff.
 
1399
        """
 
1400
        return diff.get_trees_and_branches_to_diff_locked(
 
1401
            path_list, revision_specs, old_url, new_url, self.addCleanup)
 
1402
 
 
1403
    def test_basic(self):
 
1404
        tree = self.make_branch_and_tree('tree')
 
1405
        (old_tree, new_tree,
 
1406
         old_branch, new_branch,
 
1407
         specific_files, extra_trees) = self.call_gtabtd(
 
1408
             ['tree'], None, None, None)
 
1409
 
 
1410
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1411
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1412
                         old_tree.get_revision_id())
 
1413
        self.assertEqual(tree.basedir, new_tree.basedir)
 
1414
        self.assertEqual(tree.branch.base, old_branch.base)
 
1415
        self.assertEqual(tree.branch.base, new_branch.base)
 
1416
        self.assertIs(None, specific_files)
 
1417
        self.assertIs(None, extra_trees)
 
1418
 
 
1419
    def test_with_rev_specs(self):
 
1420
        tree = self.make_branch_and_tree('tree')
 
1421
        self.build_tree_contents([('tree/file', 'oldcontent')])
 
1422
        tree.add('file', 'file-id')
 
1423
        tree.commit('old tree', timestamp=0, rev_id="old-id")
 
1424
        self.build_tree_contents([('tree/file', 'newcontent')])
 
1425
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1426
 
 
1427
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1428
                     revisionspec.RevisionSpec.from_string('2')]
 
1429
        (old_tree, new_tree,
 
1430
         old_branch, new_branch,
 
1431
         specific_files, extra_trees) = self.call_gtabtd(
 
1432
            ['tree'], revisions, None, None)
 
1433
 
 
1434
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1435
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1436
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
 
1437
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1438
        self.assertEqual(tree.branch.base, old_branch.base)
 
1439
        self.assertEqual(tree.branch.base, new_branch.base)
 
1440
        self.assertIs(None, specific_files)
 
1441
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
 
1442
 
 
1443
 
 
1444
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
 
1445
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
 
1446
    deprecated get_trees_and_branches_to_diff function.
 
1447
    """
 
1448
 
 
1449
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1450
        return self.applyDeprecated(
 
1451
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
 
1452
            path_list, revision_specs, old_url, new_url)
 
1453