~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: 2007-11-03 23:02:16 UTC
  • mfrom: (2951.1.1 pack)
  • Revision ID: pqm@pqm.ubuntu.com-20071103230216-mnmwuxm413lyhjdv
(robertc) Fix data-refresh logic for packs not to refresh mid-transaction when a names write lock is held. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
18
 
import os.path
19
18
from cStringIO import StringIO
20
19
import errno
21
20
import subprocess
22
 
import sys
23
21
from tempfile import TemporaryFile
24
22
 
25
 
from bzrlib import tests
26
 
from bzrlib.diff import (
27
 
    DiffFromTool,
28
 
    DiffPath,
29
 
    DiffSymlink,
30
 
    DiffTree,
31
 
    DiffText,
32
 
    external_diff,
33
 
    internal_diff,
34
 
    show_diff_trees,
35
 
    )
36
 
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
 
23
from bzrlib.diff import internal_diff, external_diff, show_diff_trees
 
24
from bzrlib.errors import BinaryFile, NoDiff
37
25
import bzrlib.osutils as osutils
38
 
import bzrlib.transform as transform
39
26
import bzrlib.patiencediff
40
27
import bzrlib._patiencediff_py
41
28
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
42
29
                          TestCaseInTempDir, TestSkipped)
43
30
 
44
31
 
45
 
class _AttribFeature(Feature):
46
 
 
47
 
    def _probe(self):
48
 
        if (sys.platform not in ('cygwin', 'win32')):
49
 
            return False
50
 
        try:
51
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
52
 
        except OSError, e:
53
 
            return False
54
 
        return (0 == proc.wait())
55
 
 
56
 
    def feature_name(self):
57
 
        return 'attrib Windows command-line tool'
58
 
 
59
 
AttribFeature = _AttribFeature()
60
 
 
61
 
 
62
32
class _CompiledPatienceDiffFeature(Feature):
63
33
 
64
34
    def _probe(self):
74
44
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
75
45
 
76
46
 
 
47
class _UnicodeFilename(Feature):
 
48
    """Does the filesystem support Unicode filenames?"""
 
49
 
 
50
    def _probe(self):
 
51
        try:
 
52
            os.stat(u'\u03b1')
 
53
        except UnicodeEncodeError:
 
54
            return False
 
55
        except (IOError, OSError):
 
56
            # The filesystem allows the Unicode filename but the file doesn't
 
57
            # exist.
 
58
            return True
 
59
        else:
 
60
            # The filesystem allows the Unicode filename and the file exists,
 
61
            # for some reason.
 
62
            return True
 
63
 
 
64
UnicodeFilename = _UnicodeFilename()
 
65
 
 
66
 
 
67
class TestUnicodeFilename(TestCase):
 
68
 
 
69
    def test_probe_passes(self):
 
70
        """UnicodeFilename._probe passes."""
 
71
        # We can't test much more than that because the behaviour depends
 
72
        # on the platform.
 
73
        UnicodeFilename._probe()
 
74
        
 
75
 
77
76
def udiff_lines(old, new, allow_binary=False):
78
77
    output = StringIO()
79
78
    internal_diff('old', old, 'new', new, output, allow_binary)
232
231
                          ]
233
232
                          , lines)
234
233
 
235
 
    def test_internal_diff_no_content(self):
236
 
        output = StringIO()
237
 
        internal_diff(u'old', [], u'new', [], output)
238
 
        self.assertEqual('', output.getvalue())
239
 
 
240
 
    def test_internal_diff_no_changes(self):
241
 
        output = StringIO()
242
 
        internal_diff(u'old', ['text\n', 'contents\n'],
243
 
                      u'new', ['text\n', 'contents\n'],
244
 
                      output)
245
 
        self.assertEqual('', output.getvalue())
246
 
 
247
234
    def test_internal_diff_returns_bytes(self):
248
235
        import StringIO
249
236
        output = StringIO.StringIO()
499
486
        self.assertContainsRe(diff, '-contents\n'
500
487
                                    '\\+new contents\n')
501
488
 
502
 
 
503
 
    def test_internal_diff_exec_property(self):
504
 
        tree = self.make_branch_and_tree('tree')
505
 
 
506
 
        tt = transform.TreeTransform(tree)
507
 
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
508
 
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
509
 
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
510
 
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
511
 
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
512
 
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
513
 
        tt.apply()
514
 
        tree.commit('one', rev_id='rev-1')
515
 
 
516
 
        tt = transform.TreeTransform(tree)
517
 
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
518
 
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
519
 
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
520
 
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
521
 
        tt.apply()
522
 
        tree.rename_one('c', 'new-c')
523
 
        tree.rename_one('d', 'new-d')
524
 
 
525
 
        diff = self.get_diff(tree.basis_tree(), tree)
526
 
 
527
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
528
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
529
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
530
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
531
 
        self.assertNotContainsRe(diff, r"file 'e'")
532
 
        self.assertNotContainsRe(diff, r"file 'f'")
533
 
 
534
 
 
535
489
    def test_binary_unicode_filenames(self):
536
490
        """Test that contents of files are *not* encoded in UTF-8 when there
537
491
        is a binary file in the diff.
538
492
        """
539
493
        # See https://bugs.launchpad.net/bugs/110092.
540
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
494
        self.requireFeature(UnicodeFilename)
541
495
 
542
496
        # This bug isn't triggered with cStringIO.
543
497
        from StringIO import StringIO
562
516
 
563
517
    def test_unicode_filename(self):
564
518
        """Test when the filename are unicode."""
565
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
519
        self.requireFeature(UnicodeFilename)
566
520
 
567
521
        alpha, omega = u'\u03b1', u'\u03c9'
568
522
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
590
544
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
591
545
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
592
546
 
593
 
 
594
 
class DiffWasIs(DiffPath):
595
 
 
596
 
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
597
 
        self.to_file.write('was: ')
598
 
        self.to_file.write(self.old_tree.get_file(file_id).read())
599
 
        self.to_file.write('is: ')
600
 
        self.to_file.write(self.new_tree.get_file(file_id).read())
601
 
        pass
602
 
 
603
 
 
604
 
class TestDiffTree(TestCaseWithTransport):
605
 
 
606
 
    def setUp(self):
607
 
        TestCaseWithTransport.setUp(self)
608
 
        self.old_tree = self.make_branch_and_tree('old-tree')
609
 
        self.old_tree.lock_write()
610
 
        self.addCleanup(self.old_tree.unlock)
611
 
        self.new_tree = self.make_branch_and_tree('new-tree')
612
 
        self.new_tree.lock_write()
613
 
        self.addCleanup(self.new_tree.unlock)
614
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
615
 
 
616
 
    def test_diff_text(self):
617
 
        self.build_tree_contents([('old-tree/olddir/',),
618
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
619
 
        self.old_tree.add('olddir')
620
 
        self.old_tree.add('olddir/oldfile', 'file-id')
621
 
        self.build_tree_contents([('new-tree/newdir/',),
622
 
                                  ('new-tree/newdir/newfile', 'new\n')])
623
 
        self.new_tree.add('newdir')
624
 
        self.new_tree.add('newdir/newfile', 'file-id')
625
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
626
 
        differ.diff_text('file-id', None, 'old label', 'new label')
627
 
        self.assertEqual(
628
 
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
629
 
            differ.to_file.getvalue())
630
 
        differ.to_file.seek(0)
631
 
        differ.diff_text(None, 'file-id', 'old label', 'new label')
632
 
        self.assertEqual(
633
 
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
634
 
            differ.to_file.getvalue())
635
 
        differ.to_file.seek(0)
636
 
        differ.diff_text('file-id', 'file-id', 'old label', 'new label')
637
 
        self.assertEqual(
638
 
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
639
 
            differ.to_file.getvalue())
640
 
 
641
 
    def test_diff_deletion(self):
642
 
        self.build_tree_contents([('old-tree/file', 'contents'),
643
 
                                  ('new-tree/file', 'contents')])
644
 
        self.old_tree.add('file', 'file-id')
645
 
        self.new_tree.add('file', 'file-id')
646
 
        os.unlink('new-tree/file')
647
 
        self.differ.show_diff(None)
648
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
649
 
 
650
 
    def test_diff_creation(self):
651
 
        self.build_tree_contents([('old-tree/file', 'contents'),
652
 
                                  ('new-tree/file', 'contents')])
653
 
        self.old_tree.add('file', 'file-id')
654
 
        self.new_tree.add('file', 'file-id')
655
 
        os.unlink('old-tree/file')
656
 
        self.differ.show_diff(None)
657
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
658
 
 
659
 
    def test_diff_symlink(self):
660
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
661
 
        differ.diff_symlink('old target', None)
662
 
        self.assertEqual("=== target was 'old target'\n",
663
 
                         differ.to_file.getvalue())
664
 
 
665
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
666
 
        differ.diff_symlink(None, 'new target')
667
 
        self.assertEqual("=== target is 'new target'\n",
668
 
                         differ.to_file.getvalue())
669
 
 
670
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
671
 
        differ.diff_symlink('old target', 'new target')
672
 
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
673
 
                         differ.to_file.getvalue())
674
 
 
675
 
    def test_diff(self):
676
 
        self.build_tree_contents([('old-tree/olddir/',),
677
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
678
 
        self.old_tree.add('olddir')
679
 
        self.old_tree.add('olddir/oldfile', 'file-id')
680
 
        self.build_tree_contents([('new-tree/newdir/',),
681
 
                                  ('new-tree/newdir/newfile', 'new\n')])
682
 
        self.new_tree.add('newdir')
683
 
        self.new_tree.add('newdir/newfile', 'file-id')
684
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
685
 
        self.assertContainsRe(
686
 
            self.differ.to_file.getvalue(),
687
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
688
 
             ' \@\@\n-old\n\+new\n\n')
689
 
 
690
 
    def test_diff_kind_change(self):
691
 
        self.requireFeature(tests.SymlinkFeature)
692
 
        self.build_tree_contents([('old-tree/olddir/',),
693
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
694
 
        self.old_tree.add('olddir')
695
 
        self.old_tree.add('olddir/oldfile', 'file-id')
696
 
        self.build_tree(['new-tree/newdir/'])
697
 
        os.symlink('new', 'new-tree/newdir/newfile')
698
 
        self.new_tree.add('newdir')
699
 
        self.new_tree.add('newdir/newfile', 'file-id')
700
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
701
 
        self.assertContainsRe(
702
 
            self.differ.to_file.getvalue(),
703
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
704
 
             ' \@\@\n-old\n\n')
705
 
        self.assertContainsRe(self.differ.to_file.getvalue(),
706
 
                              "=== target is 'new'\n")
707
 
 
708
 
    def test_diff_directory(self):
709
 
        self.build_tree(['new-tree/new-dir/'])
710
 
        self.new_tree.add('new-dir', 'new-dir-id')
711
 
        self.differ.diff('new-dir-id', None, 'new-dir')
712
 
        self.assertEqual(self.differ.to_file.getvalue(), '')
713
 
 
714
 
    def create_old_new(self):
715
 
        self.build_tree_contents([('old-tree/olddir/',),
716
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
717
 
        self.old_tree.add('olddir')
718
 
        self.old_tree.add('olddir/oldfile', 'file-id')
719
 
        self.build_tree_contents([('new-tree/newdir/',),
720
 
                                  ('new-tree/newdir/newfile', 'new\n')])
721
 
        self.new_tree.add('newdir')
722
 
        self.new_tree.add('newdir/newfile', 'file-id')
723
 
 
724
 
    def test_register_diff(self):
725
 
        self.create_old_new()
726
 
        old_diff_factories = DiffTree.diff_factories
727
 
        DiffTree.diff_factories=old_diff_factories[:]
728
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
729
 
        try:
730
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
731
 
        finally:
732
 
            DiffTree.diff_factories = old_diff_factories
733
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
734
 
        self.assertNotContainsRe(
735
 
            differ.to_file.getvalue(),
736
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
737
 
             ' \@\@\n-old\n\+new\n\n')
738
 
        self.assertContainsRe(differ.to_file.getvalue(),
739
 
                              'was: old\nis: new\n')
740
 
 
741
 
    def test_extra_factories(self):
742
 
        self.create_old_new()
743
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
744
 
                            extra_factories=[DiffWasIs.from_diff_tree])
745
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
746
 
        self.assertNotContainsRe(
747
 
            differ.to_file.getvalue(),
748
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
749
 
             ' \@\@\n-old\n\+new\n\n')
750
 
        self.assertContainsRe(differ.to_file.getvalue(),
751
 
                              'was: old\nis: new\n')
752
 
 
753
 
    def test_alphabetical_order(self):
754
 
        self.build_tree(['new-tree/a-file'])
755
 
        self.new_tree.add('a-file')
756
 
        self.build_tree(['old-tree/b-file'])
757
 
        self.old_tree.add('b-file')
758
 
        self.differ.show_diff(None)
759
 
        self.assertContainsRe(self.differ.to_file.getvalue(),
760
 
            '.*a-file(.|\n)*b-file')
761
 
 
762
 
 
763
547
class TestPatienceDiffLib(TestCase):
764
548
 
765
549
    def setUp(self):
769
553
        self._PatienceSequenceMatcher = \
770
554
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
771
555
 
772
 
    def test_diff_unicode_string(self):
773
 
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
774
 
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
775
 
        sm = self._PatienceSequenceMatcher(None, a, b)
776
 
        mb = sm.get_matching_blocks()
777
 
        self.assertEquals(35, len(mb))
778
 
 
779
556
    def test_unique_lcs(self):
780
557
        unique_lcs = self._unique_lcs
781
558
        self.assertEquals(unique_lcs('', ''), [])
817
594
        # This is what it currently gives:
818
595
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
819
596
 
820
 
    def assertDiffBlocks(self, a, b, expected_blocks):
821
 
        """Check that the sequence matcher returns the correct blocks.
822
 
 
823
 
        :param a: A sequence to match
824
 
        :param b: Another sequence to match
825
 
        :param expected_blocks: The expected output, not including the final
826
 
            matching block (len(a), len(b), 0)
827
 
        """
828
 
        matcher = self._PatienceSequenceMatcher(None, a, b)
829
 
        blocks = matcher.get_matching_blocks()
830
 
        last = blocks.pop()
831
 
        self.assertEqual((len(a), len(b), 0), last)
832
 
        self.assertEqual(expected_blocks, blocks)
833
 
 
834
597
    def test_matching_blocks(self):
 
598
        def chk_blocks(a, b, expected_blocks):
 
599
            # difflib always adds a signature of the total
 
600
            # length, with no matching entries at the end
 
601
            s = self._PatienceSequenceMatcher(None, a, b)
 
602
            blocks = s.get_matching_blocks()
 
603
            self.assertEquals((len(a), len(b), 0), blocks[-1])
 
604
            self.assertEquals(expected_blocks, blocks[:-1])
 
605
 
835
606
        # Some basic matching tests
836
 
        self.assertDiffBlocks('', '', [])
837
 
        self.assertDiffBlocks([], [], [])
838
 
        self.assertDiffBlocks('abc', '', [])
839
 
        self.assertDiffBlocks('', 'abc', [])
840
 
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
841
 
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
842
 
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
843
 
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
844
 
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
845
 
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
846
 
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
847
 
        # This may check too much, but it checks to see that
 
607
        chk_blocks('', '', [])
 
608
        chk_blocks([], [], [])
 
609
        chk_blocks('abc', '', [])
 
610
        chk_blocks('', 'abc', [])
 
611
        chk_blocks('abcd', 'abcd', [(0, 0, 4)])
 
612
        chk_blocks('abcd', 'abce', [(0, 0, 3)])
 
613
        chk_blocks('eabc', 'abce', [(1, 0, 3)])
 
614
        chk_blocks('eabce', 'abce', [(1, 0, 4)])
 
615
        chk_blocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
616
        chk_blocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
617
        chk_blocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
618
        # This may check too much, but it checks to see that 
848
619
        # a copied block stays attached to the previous section,
849
620
        # not the later one.
850
621
        # difflib would tend to grab the trailing longest match
851
622
        # which would make the diff not look right
852
 
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
853
 
                              [(0, 0, 6), (6, 11, 10)])
 
623
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
624
                   [(0, 0, 6), (6, 11, 10)])
854
625
 
855
626
        # make sure it supports passing in lists
856
 
        self.assertDiffBlocks(
 
627
        chk_blocks(
857
628
                   ['hello there\n',
858
629
                    'world\n',
859
630
                    'how are you today?\n'],
863
634
 
864
635
        # non unique lines surrounded by non-matching lines
865
636
        # won't be found
866
 
        self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
 
637
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
867
638
 
868
639
        # But they only need to be locally unique
869
 
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
 
640
        chk_blocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
870
641
 
871
642
        # non unique blocks won't be matched
872
 
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
 
643
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
873
644
 
874
645
        # but locally unique ones will
875
 
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
 
646
        chk_blocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
876
647
                                              (5,4,1), (7,5,2), (10,8,1)])
877
648
 
878
 
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
879
 
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
880
 
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
881
 
 
882
 
    def test_matching_blocks_tuples(self):
883
 
        # Some basic matching tests
884
 
        self.assertDiffBlocks([], [], [])
885
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
886
 
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
887
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
888
 
                              [('a',), ('b',), ('c,')],
889
 
                              [(0, 0, 3)])
890
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
891
 
                              [('a',), ('b',), ('d,')],
892
 
                              [(0, 0, 2)])
893
 
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
894
 
                              [('a',), ('b',), ('c,')],
895
 
                              [(1, 1, 2)])
896
 
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
897
 
                              [('a',), ('b',), ('c,')],
898
 
                              [(1, 0, 3)])
899
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
900
 
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
901
 
                              [(0, 0, 1), (2, 2, 1)])
902
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
903
 
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
904
 
                              [(0, 0, 1), (2, 2, 1)])
 
649
        chk_blocks('abbabbXd', 'cabbabxd', [(7,7,1)])
 
650
        chk_blocks('abbabbbb', 'cabbabbc', [])
 
651
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [])
905
652
 
906
653
    def test_opcodes(self):
907
654
        def chk_ops(a, b, expected_codes):
1019
766
    def test_multiple_ranges(self):
1020
767
        # There was an earlier bug where we used a bad set of ranges,
1021
768
        # this triggers that specific bug, to make sure it doesn't regress
1022
 
        self.assertDiffBlocks('abcdefghijklmnop',
1023
 
                              'abcXghiYZQRSTUVWXYZijklmnop',
1024
 
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1025
 
 
1026
 
        self.assertDiffBlocks('ABCd efghIjk  L',
1027
 
                              'AxyzBCn mo pqrstuvwI1 2  L',
1028
 
                              [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
769
        def chk_blocks(a, b, expected_blocks):
 
770
            # difflib always adds a signature of the total
 
771
            # length, with no matching entries at the end
 
772
            s = self._PatienceSequenceMatcher(None, a, b)
 
773
            blocks = s.get_matching_blocks()
 
774
            x = blocks.pop()
 
775
            self.assertEquals(x, (len(a), len(b), 0))
 
776
            self.assertEquals(expected_blocks, blocks)
 
777
 
 
778
        chk_blocks('abcdefghijklmnop'
 
779
                 , 'abcXghiYZQRSTUVWXYZijklmnop'
 
780
                 , [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
781
 
 
782
        chk_blocks('ABCd efghIjk  L'
 
783
                 , 'AxyzBCn mo pqrstuvwI1 2  L'
 
784
                 , [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1029
785
 
1030
786
        # These are rot13 code snippets.
1031
 
        self.assertDiffBlocks('''\
 
787
        chk_blocks('''\
1032
788
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1033
789
    """
1034
790
    gnxrf_netf = ['svyr*']
1141
897
        self._PatienceSequenceMatcher = \
1142
898
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1143
899
 
1144
 
    def test_unhashable(self):
1145
 
        """We should get a proper exception here."""
1146
 
        # We need to be able to hash items in the sequence, lists are
1147
 
        # unhashable, and thus cannot be diffed
1148
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1149
 
                                         None, [[]], [])
1150
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1151
 
                                         None, ['valid', []], [])
1152
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1153
 
                                         None, ['valid'], [[]])
1154
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1155
 
                                         None, ['valid'], ['valid', []])
1156
 
 
1157
900
 
1158
901
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1159
902
 
1267
1010
            from bzrlib._patiencediff_py import recurse_matches_py
1268
1011
            self.assertIs(recurse_matches_py,
1269
1012
                          bzrlib.patiencediff.recurse_matches)
1270
 
 
1271
 
 
1272
 
class TestDiffFromTool(TestCaseWithTransport):
1273
 
 
1274
 
    def test_from_string(self):
1275
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1276
 
        self.addCleanup(diff_obj.finish)
1277
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1278
 
            diff_obj.command_template)
1279
 
 
1280
 
    def test_from_string_u5(self):
1281
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1282
 
        self.addCleanup(diff_obj.finish)
1283
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1284
 
                         diff_obj.command_template)
1285
 
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1286
 
                         diff_obj._get_command('old-path', 'new-path'))
1287
 
 
1288
 
    def test_execute(self):
1289
 
        output = StringIO()
1290
 
        diff_obj = DiffFromTool(['python', '-c',
1291
 
                                 'print "%(old_path)s %(new_path)s"'],
1292
 
                                None, None, output)
1293
 
        self.addCleanup(diff_obj.finish)
1294
 
        diff_obj._execute('old', 'new')
1295
 
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1296
 
 
1297
 
    def test_excute_missing(self):
1298
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1299
 
                                None, None, None)
1300
 
        self.addCleanup(diff_obj.finish)
1301
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1302
 
                              'new')
1303
 
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1304
 
                         ' on this machine', str(e))
1305
 
 
1306
 
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1307
 
        self.requireFeature(AttribFeature)
1308
 
        output = StringIO()
1309
 
        tree = self.make_branch_and_tree('tree')
1310
 
        self.build_tree_contents([('tree/file', 'content')])
1311
 
        tree.add('file', 'file-id')
1312
 
        tree.commit('old tree')
1313
 
        tree.lock_read()
1314
 
        self.addCleanup(tree.unlock)
1315
 
        diff_obj = DiffFromTool(['python', '-c',
1316
 
                                 'print "%(old_path)s %(new_path)s"'],
1317
 
                                tree, tree, output)
1318
 
        diff_obj._prepare_files('file-id', 'file', 'file')
1319
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1320
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
1321
 
 
1322
 
    def assertReadableByAttrib(self, cwd, relpath, regex):
1323
 
        proc = subprocess.Popen(['attrib', relpath],
1324
 
                                stdout=subprocess.PIPE,
1325
 
                                cwd=cwd)
1326
 
        proc.wait()
1327
 
        result = proc.stdout.read()
1328
 
        self.assertContainsRe(result, regex)
1329
 
 
1330
 
    def test_prepare_files(self):
1331
 
        output = StringIO()
1332
 
        tree = self.make_branch_and_tree('tree')
1333
 
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1334
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1335
 
        tree.add('oldname', 'file-id')
1336
 
        tree.add('oldname2', 'file2-id')
1337
 
        tree.commit('old tree', timestamp=0)
1338
 
        tree.rename_one('oldname', 'newname')
1339
 
        tree.rename_one('oldname2', 'newname2')
1340
 
        self.build_tree_contents([('tree/newname', 'newcontent')])
1341
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1342
 
        old_tree = tree.basis_tree()
1343
 
        old_tree.lock_read()
1344
 
        self.addCleanup(old_tree.unlock)
1345
 
        tree.lock_read()
1346
 
        self.addCleanup(tree.unlock)
1347
 
        diff_obj = DiffFromTool(['python', '-c',
1348
 
                                 'print "%(old_path)s %(new_path)s"'],
1349
 
                                old_tree, tree, output)
1350
 
        self.addCleanup(diff_obj.finish)
1351
 
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1352
 
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1353
 
                                                     'newname')
1354
 
        self.assertContainsRe(old_path, 'old/oldname$')
1355
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1356
 
        self.assertContainsRe(new_path, 'new/newname$')
1357
 
        self.assertFileEqual('oldcontent', old_path)
1358
 
        self.assertFileEqual('newcontent', new_path)
1359
 
        if osutils.host_os_dereferences_symlinks():
1360
 
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1361
 
        # make sure we can create files with the same parent directories
1362
 
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')