~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge bzr.dev

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
37
 
import bzrlib.osutils as osutils
38
 
import bzrlib.transform as transform
 
23
from bzrlib.diff import internal_diff, external_diff, show_diff_trees
 
24
from bzrlib.errors import BinaryFile, NoDiff
39
25
import bzrlib.patiencediff
40
 
import bzrlib._patiencediff_py
41
 
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
 
26
from bzrlib.tests import (TestCase, TestCaseWithTransport,
42
27
                          TestCaseInTempDir, TestSkipped)
43
28
 
44
29
 
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
 
class _CompiledPatienceDiffFeature(Feature):
63
 
 
64
 
    def _probe(self):
65
 
        try:
66
 
            import bzrlib._patiencediff_c
67
 
        except ImportError:
68
 
            return False
69
 
        return True
70
 
 
71
 
    def feature_name(self):
72
 
        return 'bzrlib._patiencediff_c'
73
 
 
74
 
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
75
 
 
76
 
 
77
30
def udiff_lines(old, new, allow_binary=False):
78
31
    output = StringIO()
79
32
    internal_diff('old', old, 'new', new, output, allow_binary)
158
111
        self.check_patch(lines)
159
112
 
160
113
    def test_external_diff_binary_lang_c(self):
161
 
        old_env = {}
162
 
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
163
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
 
114
        orig_lang = os.environ.get('LANG')
 
115
        orig_lc_all = os.environ.get('LC_ALL')
164
116
        try:
 
117
            os.environ['LANG'] = 'C'
 
118
            os.environ['LC_ALL'] = 'C'
165
119
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
166
120
            # Older versions of diffutils say "Binary files", newer
167
121
            # versions just say "Files".
169
123
                                  '(Binary f|F)iles old and new differ\n')
170
124
            self.assertEquals(lines[1:], ['\n'])
171
125
        finally:
172
 
            for lang, old_val in old_env.iteritems():
173
 
                osutils.set_or_unset_env(lang, old_val)
 
126
            for name, value in [('LANG', orig_lang), ('LC_ALL', orig_lc_all)]:
 
127
                if value is None:
 
128
                    del os.environ[name]
 
129
                else:
 
130
                    os.environ[name] = value
174
131
 
175
132
    def test_no_external_diff(self):
176
133
        """Check that NoDiff is raised when diff is not available"""
232
189
                          ]
233
190
                          , lines)
234
191
 
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
192
    def test_internal_diff_returns_bytes(self):
248
193
        import StringIO
249
194
        output = StringIO.StringIO()
272
217
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
273
218
 
274
219
 
275
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
276
 
    """Has a helper for running show_diff_trees"""
277
 
 
278
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
279
 
        output = StringIO()
280
 
        if working_tree is not None:
281
 
            extra_trees = (working_tree,)
282
 
        else:
283
 
            extra_trees = ()
284
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
285
 
                        extra_trees=extra_trees, old_label='old/',
286
 
                        new_label='new/')
287
 
        return output.getvalue()
288
 
 
289
 
 
290
 
class TestDiffDates(TestShowDiffTreesHelper):
 
220
class TestDiffDates(TestCaseWithTransport):
291
221
 
292
222
    def setUp(self):
293
223
        super(TestDiffDates, self).setUp()
327
257
        # set the date stamps for files in the working tree to known values
328
258
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
329
259
 
 
260
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
 
261
        output = StringIO()
 
262
        if working_tree is not None:
 
263
            extra_trees = (working_tree,)
 
264
        else:
 
265
            extra_trees = ()
 
266
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
 
267
                        extra_trees=extra_trees, old_label='old/', 
 
268
                        new_label='new/')
 
269
        return output.getvalue()
 
270
 
330
271
    def test_diff_rev_tree_working_tree(self):
331
272
        output = self.get_diff(self.wt.basis_tree(), self.wt)
332
273
        # note that the date for old/file1 is from rev 2 rather than from
416
357
        self.assertNotContainsRe(out, 'file1\t')
417
358
 
418
359
 
419
 
 
420
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
421
 
    """Direct tests for show_diff_trees"""
422
 
 
423
 
    def test_modified_file(self):
424
 
        """Test when a file is modified."""
425
 
        tree = self.make_branch_and_tree('tree')
426
 
        self.build_tree_contents([('tree/file', 'contents\n')])
427
 
        tree.add(['file'], ['file-id'])
428
 
        tree.commit('one', rev_id='rev-1')
429
 
 
430
 
        self.build_tree_contents([('tree/file', 'new contents\n')])
431
 
        diff = self.get_diff(tree.basis_tree(), tree)
432
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
433
 
        self.assertContainsRe(diff, '--- old/file\t')
434
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
435
 
        self.assertContainsRe(diff, '-contents\n'
436
 
                                    '\\+new contents\n')
437
 
 
438
 
    def test_modified_file_in_renamed_dir(self):
439
 
        """Test when a file is modified in a renamed directory."""
440
 
        tree = self.make_branch_and_tree('tree')
441
 
        self.build_tree(['tree/dir/'])
442
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
443
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
444
 
        tree.commit('one', rev_id='rev-1')
445
 
 
446
 
        tree.rename_one('dir', 'other')
447
 
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
448
 
        diff = self.get_diff(tree.basis_tree(), tree)
449
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
450
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
451
 
        # XXX: This is technically incorrect, because it used to be at another
452
 
        # location. What to do?
453
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
454
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
455
 
        self.assertContainsRe(diff, '-contents\n'
456
 
                                    '\\+new contents\n')
457
 
 
458
 
    def test_renamed_directory(self):
459
 
        """Test when only a directory is only renamed."""
460
 
        tree = self.make_branch_and_tree('tree')
461
 
        self.build_tree(['tree/dir/'])
462
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
463
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
464
 
        tree.commit('one', rev_id='rev-1')
465
 
 
466
 
        tree.rename_one('dir', 'newdir')
467
 
        diff = self.get_diff(tree.basis_tree(), tree)
468
 
        # Renaming a directory should be a single "you renamed this dir" even
469
 
        # when there are files inside.
470
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
471
 
 
472
 
    def test_renamed_file(self):
473
 
        """Test when a file is only renamed."""
474
 
        tree = self.make_branch_and_tree('tree')
475
 
        self.build_tree_contents([('tree/file', 'contents\n')])
476
 
        tree.add(['file'], ['file-id'])
477
 
        tree.commit('one', rev_id='rev-1')
478
 
 
479
 
        tree.rename_one('file', 'newname')
480
 
        diff = self.get_diff(tree.basis_tree(), tree)
481
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
482
 
        # We shouldn't have a --- or +++ line, because there is no content
483
 
        # change
484
 
        self.assertNotContainsRe(diff, '---')
485
 
 
486
 
    def test_renamed_and_modified_file(self):
487
 
        """Test when a file is only renamed."""
488
 
        tree = self.make_branch_and_tree('tree')
489
 
        self.build_tree_contents([('tree/file', 'contents\n')])
490
 
        tree.add(['file'], ['file-id'])
491
 
        tree.commit('one', rev_id='rev-1')
492
 
 
493
 
        tree.rename_one('file', 'newname')
494
 
        self.build_tree_contents([('tree/newname', 'new contents\n')])
495
 
        diff = self.get_diff(tree.basis_tree(), tree)
496
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
497
 
        self.assertContainsRe(diff, '--- old/file\t')
498
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
499
 
        self.assertContainsRe(diff, '-contents\n'
500
 
                                    '\\+new contents\n')
501
 
 
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
 
    def test_binary_unicode_filenames(self):
536
 
        """Test that contents of files are *not* encoded in UTF-8 when there
537
 
        is a binary file in the diff.
538
 
        """
539
 
        # See https://bugs.launchpad.net/bugs/110092.
540
 
        self.requireFeature(tests.UnicodeFilenameFeature)
541
 
 
542
 
        # This bug isn't triggered with cStringIO.
543
 
        from StringIO import StringIO
544
 
        tree = self.make_branch_and_tree('tree')
545
 
        alpha, omega = u'\u03b1', u'\u03c9'
546
 
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
547
 
        self.build_tree_contents(
548
 
            [('tree/' + alpha, chr(0)),
549
 
             ('tree/' + omega,
550
 
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
551
 
        tree.add([alpha], ['file-id'])
552
 
        tree.add([omega], ['file-id-2'])
553
 
        diff_content = StringIO()
554
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
555
 
        diff = diff_content.getvalue()
556
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
557
 
        self.assertContainsRe(
558
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
559
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
560
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
561
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
562
 
 
563
 
    def test_unicode_filename(self):
564
 
        """Test when the filename are unicode."""
565
 
        self.requireFeature(tests.UnicodeFilenameFeature)
566
 
 
567
 
        alpha, omega = u'\u03b1', u'\u03c9'
568
 
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
569
 
 
570
 
        tree = self.make_branch_and_tree('tree')
571
 
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
572
 
        tree.add(['ren_'+alpha], ['file-id-2'])
573
 
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
574
 
        tree.add(['del_'+alpha], ['file-id-3'])
575
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
576
 
        tree.add(['mod_'+alpha], ['file-id-4'])
577
 
 
578
 
        tree.commit('one', rev_id='rev-1')
579
 
 
580
 
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
581
 
        tree.remove('del_'+alpha)
582
 
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
583
 
        tree.add(['add_'+alpha], ['file-id'])
584
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
585
 
 
586
 
        diff = self.get_diff(tree.basis_tree(), tree)
587
 
        self.assertContainsRe(diff,
588
 
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
589
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
590
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
591
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
592
 
 
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
360
class TestPatienceDiffLib(TestCase):
764
361
 
765
 
    def setUp(self):
766
 
        super(TestPatienceDiffLib, self).setUp()
767
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
768
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
769
 
        self._PatienceSequenceMatcher = \
770
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
771
 
 
772
362
    def test_unique_lcs(self):
773
 
        unique_lcs = self._unique_lcs
 
363
        unique_lcs = bzrlib.patiencediff.unique_lcs
774
364
        self.assertEquals(unique_lcs('', ''), [])
775
 
        self.assertEquals(unique_lcs('', 'a'), [])
776
 
        self.assertEquals(unique_lcs('a', ''), [])
777
365
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
778
366
        self.assertEquals(unique_lcs('a', 'b'), [])
779
367
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
786
374
    def test_recurse_matches(self):
787
375
        def test_one(a, b, matches):
788
376
            test_matches = []
789
 
            self._recurse_matches(
790
 
                a, b, 0, 0, len(a), len(b), test_matches, 10)
 
377
            bzrlib.patiencediff.recurse_matches(a, b, 0, 0, len(a), len(b),
 
378
                test_matches, 10)
791
379
            self.assertEquals(test_matches, matches)
792
380
 
793
381
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
794
382
                 [(0, 0), (2, 2), (4, 4)])
795
383
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
796
384
                 [(0, 0), (2, 1), (4, 2)])
797
 
        # Even though 'bc' is not unique globally, and is surrounded by
798
 
        # non-matching lines, we should still match, because they are locally
799
 
        # unique
800
 
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
801
 
                                          (4, 6), (5, 7), (6, 8)])
802
385
 
803
386
        # recurse_matches doesn't match non-unique 
804
387
        # lines surrounded by bogus text.
810
393
        # This is what it currently gives:
811
394
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
812
395
 
813
 
    def assertDiffBlocks(self, a, b, expected_blocks):
814
 
        """Check that the sequence matcher returns the correct blocks.
815
 
 
816
 
        :param a: A sequence to match
817
 
        :param b: Another sequence to match
818
 
        :param expected_blocks: The expected output, not including the final
819
 
            matching block (len(a), len(b), 0)
820
 
        """
821
 
        matcher = self._PatienceSequenceMatcher(None, a, b)
822
 
        blocks = matcher.get_matching_blocks()
823
 
        last = blocks.pop()
824
 
        self.assertEqual((len(a), len(b), 0), last)
825
 
        self.assertEqual(expected_blocks, blocks)
826
 
 
827
396
    def test_matching_blocks(self):
 
397
        def chk_blocks(a, b, expected_blocks):
 
398
            # difflib always adds a signature of the total
 
399
            # length, with no matching entries at the end
 
400
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
 
401
            blocks = s.get_matching_blocks()
 
402
            self.assertEquals((len(a), len(b), 0), blocks[-1])
 
403
            self.assertEquals(expected_blocks, blocks[:-1])
 
404
 
828
405
        # Some basic matching tests
829
 
        self.assertDiffBlocks('', '', [])
830
 
        self.assertDiffBlocks([], [], [])
831
 
        self.assertDiffBlocks('abc', '', [])
832
 
        self.assertDiffBlocks('', 'abc', [])
833
 
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
834
 
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
835
 
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
836
 
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
837
 
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
838
 
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
839
 
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
840
 
        # This may check too much, but it checks to see that
 
406
        chk_blocks('', '', [])
 
407
        chk_blocks([], [], [])
 
408
        chk_blocks('abcd', 'abcd', [(0, 0, 4)])
 
409
        chk_blocks('abcd', 'abce', [(0, 0, 3)])
 
410
        chk_blocks('eabc', 'abce', [(1, 0, 3)])
 
411
        chk_blocks('eabce', 'abce', [(1, 0, 4)])
 
412
        chk_blocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
413
        chk_blocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
414
        chk_blocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
415
        # This may check too much, but it checks to see that 
841
416
        # a copied block stays attached to the previous section,
842
417
        # not the later one.
843
418
        # difflib would tend to grab the trailing longest match
844
419
        # which would make the diff not look right
845
 
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
846
 
                              [(0, 0, 6), (6, 11, 10)])
 
420
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
421
                   [(0, 0, 6), (6, 11, 10)])
847
422
 
848
423
        # make sure it supports passing in lists
849
 
        self.assertDiffBlocks(
 
424
        chk_blocks(
850
425
                   ['hello there\n',
851
426
                    'world\n',
852
427
                    'how are you today?\n'],
856
431
 
857
432
        # non unique lines surrounded by non-matching lines
858
433
        # won't be found
859
 
        self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
 
434
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
860
435
 
861
436
        # But they only need to be locally unique
862
 
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
 
437
        chk_blocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
863
438
 
864
439
        # non unique blocks won't be matched
865
 
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
 
440
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
866
441
 
867
442
        # but locally unique ones will
868
 
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
 
443
        chk_blocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
869
444
                                              (5,4,1), (7,5,2), (10,8,1)])
870
445
 
871
 
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
872
 
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
873
 
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
874
 
 
875
 
    def test_matching_blocks_tuples(self):
876
 
        # Some basic matching tests
877
 
        self.assertDiffBlocks([], [], [])
878
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
879
 
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
880
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
881
 
                              [('a',), ('b',), ('c,')],
882
 
                              [(0, 0, 3)])
883
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
884
 
                              [('a',), ('b',), ('d,')],
885
 
                              [(0, 0, 2)])
886
 
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
887
 
                              [('a',), ('b',), ('c,')],
888
 
                              [(1, 1, 2)])
889
 
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
890
 
                              [('a',), ('b',), ('c,')],
891
 
                              [(1, 0, 3)])
892
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
893
 
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
894
 
                              [(0, 0, 1), (2, 2, 1)])
895
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
896
 
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
897
 
                              [(0, 0, 1), (2, 2, 1)])
 
446
        chk_blocks('abbabbXd', 'cabbabxd', [(7,7,1)])
 
447
        chk_blocks('abbabbbb', 'cabbabbc', [])
 
448
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [])
898
449
 
899
450
    def test_opcodes(self):
900
451
        def chk_ops(a, b, expected_codes):
901
 
            s = self._PatienceSequenceMatcher(None, a, b)
 
452
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
902
453
            self.assertEquals(expected_codes, s.get_opcodes())
903
454
 
904
455
        chk_ops('', '', [])
905
456
        chk_ops([], [], [])
906
 
        chk_ops('abc', '', [('delete', 0,3, 0,0)])
907
 
        chk_ops('', 'abc', [('insert', 0,0, 0,3)])
908
457
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
909
458
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
910
459
                                 ('replace', 3,4, 3,4)
972
521
                 ('equal',   10,11, 8,9)
973
522
                ])
974
523
 
975
 
    def test_grouped_opcodes(self):
976
 
        def chk_ops(a, b, expected_codes, n=3):
977
 
            s = self._PatienceSequenceMatcher(None, a, b)
978
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
979
 
 
980
 
        chk_ops('', '', [])
981
 
        chk_ops([], [], [])
982
 
        chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
983
 
        chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
984
 
        chk_ops('abcd', 'abcd', [])
985
 
        chk_ops('abcd', 'abce', [[('equal',   0,3, 0,3),
986
 
                                  ('replace', 3,4, 3,4)
987
 
                                 ]])
988
 
        chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
989
 
                                 ('equal',  1,4, 0,3),
990
 
                                 ('insert', 4,4, 3,4)
991
 
                                ]])
992
 
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
993
 
                [[('equal',  3,6, 3,6),
994
 
                  ('insert', 6,6, 6,11),
995
 
                  ('equal',  6,9, 11,14)
996
 
                  ]])
997
 
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
998
 
                [[('equal',  2,6, 2,6),
999
 
                  ('insert', 6,6, 6,11),
1000
 
                  ('equal',  6,10, 11,15)
1001
 
                  ]], 4)
1002
 
        chk_ops('Xabcdef', 'abcdef',
1003
 
                [[('delete', 0,1, 0,0),
1004
 
                  ('equal',  1,4, 0,3)
1005
 
                  ]])
1006
 
        chk_ops('abcdef', 'abcdefX',
1007
 
                [[('equal',  3,6, 3,6),
1008
 
                  ('insert', 6,6, 6,7)
1009
 
                  ]])
1010
 
 
1011
 
 
1012
524
    def test_multiple_ranges(self):
1013
525
        # There was an earlier bug where we used a bad set of ranges,
1014
526
        # this triggers that specific bug, to make sure it doesn't regress
1015
 
        self.assertDiffBlocks('abcdefghijklmnop',
1016
 
                              'abcXghiYZQRSTUVWXYZijklmnop',
1017
 
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1018
 
 
1019
 
        self.assertDiffBlocks('ABCd efghIjk  L',
1020
 
                              'AxyzBCn mo pqrstuvwI1 2  L',
1021
 
                              [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
527
        def chk_blocks(a, b, expected_blocks):
 
528
            # difflib always adds a signature of the total
 
529
            # length, with no matching entries at the end
 
530
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
 
531
            blocks = s.get_matching_blocks()
 
532
            x = blocks.pop()
 
533
            self.assertEquals(x, (len(a), len(b), 0))
 
534
            self.assertEquals(expected_blocks, blocks)
 
535
 
 
536
        chk_blocks('abcdefghijklmnop'
 
537
                 , 'abcXghiYZQRSTUVWXYZijklmnop'
 
538
                 , [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
539
 
 
540
        chk_blocks('ABCd efghIjk  L'
 
541
                 , 'AxyzBCn mo pqrstuvwI1 2  L'
 
542
                 , [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1022
543
 
1023
544
        # These are rot13 code snippets.
1024
 
        self.assertDiffBlocks('''\
 
545
        chk_blocks('''\
1025
546
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1026
547
    """
1027
548
    gnxrf_netf = ['svyr*']
1074
595
        txt_b = ['hello there\n',
1075
596
                 'how are you today?\n']
1076
597
        unified_diff = bzrlib.patiencediff.unified_diff
1077
 
        psm = self._PatienceSequenceMatcher
 
598
        psm = bzrlib.patiencediff.PatienceSequenceMatcher
1078
599
        self.assertEquals([ '---  \n',
1079
600
                           '+++  \n',
1080
601
                           '@@ -1,3 +1,2 @@\n',
1122
643
                                 sequencematcher=psm)))
1123
644
 
1124
645
 
1125
 
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1126
 
 
1127
 
    _test_needs_features = [CompiledPatienceDiffFeature]
1128
 
 
1129
 
    def setUp(self):
1130
 
        super(TestPatienceDiffLib_c, self).setUp()
1131
 
        import bzrlib._patiencediff_c
1132
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1133
 
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
1134
 
        self._PatienceSequenceMatcher = \
1135
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1136
 
 
1137
 
    def test_unhashable(self):
1138
 
        """We should get a proper exception here."""
1139
 
        # We need to be able to hash items in the sequence, lists are
1140
 
        # unhashable, and thus cannot be diffed
1141
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1142
 
                                         None, [[]], [])
1143
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1144
 
                                         None, ['valid', []], [])
1145
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1146
 
                                         None, ['valid'], [[]])
1147
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1148
 
                                         None, ['valid'], ['valid', []])
1149
 
 
1150
 
 
1151
646
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1152
647
 
1153
 
    def setUp(self):
1154
 
        super(TestPatienceDiffLibFiles, self).setUp()
1155
 
        self._PatienceSequenceMatcher = \
1156
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1157
 
 
1158
648
    def test_patience_unified_diff_files(self):
1159
649
        txt_a = ['hello there\n',
1160
650
                 'world\n',
1165
655
        open('b1', 'wb').writelines(txt_b)
1166
656
 
1167
657
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1168
 
        psm = self._PatienceSequenceMatcher
 
658
        psm = bzrlib.patiencediff.PatienceSequenceMatcher
1169
659
        self.assertEquals(['--- a1 \n',
1170
660
                           '+++ b1 \n',
1171
661
                           '@@ -1,3 +1,2 @@\n',
1216
706
                          ]
1217
707
                          , list(unified_diff_files('a2', 'b2',
1218
708
                                 sequencematcher=psm)))
1219
 
 
1220
 
 
1221
 
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1222
 
 
1223
 
    _test_needs_features = [CompiledPatienceDiffFeature]
1224
 
 
1225
 
    def setUp(self):
1226
 
        super(TestPatienceDiffLibFiles_c, self).setUp()
1227
 
        import bzrlib._patiencediff_c
1228
 
        self._PatienceSequenceMatcher = \
1229
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1230
 
 
1231
 
 
1232
 
class TestUsingCompiledIfAvailable(TestCase):
1233
 
 
1234
 
    def test_PatienceSequenceMatcher(self):
1235
 
        if CompiledPatienceDiffFeature.available():
1236
 
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1237
 
            self.assertIs(PatienceSequenceMatcher_c,
1238
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1239
 
        else:
1240
 
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1241
 
            self.assertIs(PatienceSequenceMatcher_py,
1242
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1243
 
 
1244
 
    def test_unique_lcs(self):
1245
 
        if CompiledPatienceDiffFeature.available():
1246
 
            from bzrlib._patiencediff_c import unique_lcs_c
1247
 
            self.assertIs(unique_lcs_c,
1248
 
                          bzrlib.patiencediff.unique_lcs)
1249
 
        else:
1250
 
            from bzrlib._patiencediff_py import unique_lcs_py
1251
 
            self.assertIs(unique_lcs_py,
1252
 
                          bzrlib.patiencediff.unique_lcs)
1253
 
 
1254
 
    def test_recurse_matches(self):
1255
 
        if CompiledPatienceDiffFeature.available():
1256
 
            from bzrlib._patiencediff_c import recurse_matches_c
1257
 
            self.assertIs(recurse_matches_c,
1258
 
                          bzrlib.patiencediff.recurse_matches)
1259
 
        else:
1260
 
            from bzrlib._patiencediff_py import recurse_matches_py
1261
 
            self.assertIs(recurse_matches_py,
1262
 
                          bzrlib.patiencediff.recurse_matches)
1263
 
 
1264
 
 
1265
 
class TestDiffFromTool(TestCaseWithTransport):
1266
 
 
1267
 
    def test_from_string(self):
1268
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1269
 
        self.addCleanup(diff_obj.finish)
1270
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1271
 
            diff_obj.command_template)
1272
 
 
1273
 
    def test_from_string_u5(self):
1274
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1275
 
        self.addCleanup(diff_obj.finish)
1276
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1277
 
                         diff_obj.command_template)
1278
 
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1279
 
                         diff_obj._get_command('old-path', 'new-path'))
1280
 
 
1281
 
    def test_execute(self):
1282
 
        output = StringIO()
1283
 
        diff_obj = DiffFromTool(['python', '-c',
1284
 
                                 'print "%(old_path)s %(new_path)s"'],
1285
 
                                None, None, output)
1286
 
        self.addCleanup(diff_obj.finish)
1287
 
        diff_obj._execute('old', 'new')
1288
 
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1289
 
 
1290
 
    def test_excute_missing(self):
1291
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1292
 
                                None, None, None)
1293
 
        self.addCleanup(diff_obj.finish)
1294
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1295
 
                              'new')
1296
 
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1297
 
                         ' on this machine', str(e))
1298
 
 
1299
 
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1300
 
        self.requireFeature(AttribFeature)
1301
 
        output = StringIO()
1302
 
        tree = self.make_branch_and_tree('tree')
1303
 
        self.build_tree_contents([('tree/file', 'content')])
1304
 
        tree.add('file', 'file-id')
1305
 
        tree.commit('old tree')
1306
 
        tree.lock_read()
1307
 
        self.addCleanup(tree.unlock)
1308
 
        diff_obj = DiffFromTool(['python', '-c',
1309
 
                                 'print "%(old_path)s %(new_path)s"'],
1310
 
                                tree, tree, output)
1311
 
        diff_obj._prepare_files('file-id', 'file', 'file')
1312
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1313
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
1314
 
 
1315
 
    def assertReadableByAttrib(self, cwd, relpath, regex):
1316
 
        proc = subprocess.Popen(['attrib', relpath],
1317
 
                                stdout=subprocess.PIPE,
1318
 
                                cwd=cwd)
1319
 
        proc.wait()
1320
 
        result = proc.stdout.read()
1321
 
        self.assertContainsRe(result, regex)
1322
 
 
1323
 
    def test_prepare_files(self):
1324
 
        output = StringIO()
1325
 
        tree = self.make_branch_and_tree('tree')
1326
 
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1327
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1328
 
        tree.add('oldname', 'file-id')
1329
 
        tree.add('oldname2', 'file2-id')
1330
 
        tree.commit('old tree', timestamp=0)
1331
 
        tree.rename_one('oldname', 'newname')
1332
 
        tree.rename_one('oldname2', 'newname2')
1333
 
        self.build_tree_contents([('tree/newname', 'newcontent')])
1334
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1335
 
        old_tree = tree.basis_tree()
1336
 
        old_tree.lock_read()
1337
 
        self.addCleanup(old_tree.unlock)
1338
 
        tree.lock_read()
1339
 
        self.addCleanup(tree.unlock)
1340
 
        diff_obj = DiffFromTool(['python', '-c',
1341
 
                                 'print "%(old_path)s %(new_path)s"'],
1342
 
                                old_tree, tree, output)
1343
 
        self.addCleanup(diff_obj.finish)
1344
 
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1345
 
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1346
 
                                                     'newname')
1347
 
        self.assertContainsRe(old_path, 'old/oldname$')
1348
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1349
 
        self.assertContainsRe(new_path, 'new/newname$')
1350
 
        self.assertFileEqual('oldcontent', old_path)
1351
 
        self.assertFileEqual('newcontent', new_path)
1352
 
        if osutils.host_os_dereferences_symlinks():
1353
 
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1354
 
        # make sure we can create files with the same parent directories
1355
 
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')