~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: 2009-06-03 15:02:09 UTC
  • mfrom: (4398.2.1 export-test-fix)
  • Revision ID: pqm@pqm.ubuntu.com-20090603150209-szap3popp2j8fpl3
(John Szakmeister) Fix error formatting for tar related KnowFailure
        on Mac

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
 
18
import os.path
18
19
from cStringIO import StringIO
 
20
import errno
19
21
import subprocess
20
22
import sys
21
 
import tempfile
 
23
from tempfile import TemporaryFile
22
24
 
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,
 
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,
34
35
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import features
37
 
from bzrlib.tests.blackbox.test_diff import subst_dates
38
 
 
39
 
 
40
 
class _AttribFeature(tests.Feature):
 
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):
41
47
 
42
48
    def _probe(self):
43
49
        if (sys.platform not in ('cygwin', 'win32')):
54
60
AttribFeature = _AttribFeature()
55
61
 
56
62
 
57
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
58
 
                                    'bzrlib._patiencediff_c')
 
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()
59
76
 
60
77
 
61
78
def udiff_lines(old, new, allow_binary=False):
62
79
    output = StringIO()
63
 
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
 
80
    internal_diff('old', old, 'new', new, output, allow_binary)
64
81
    output.seek(0, 0)
65
82
    return output.readlines()
66
83
 
70
87
        # StringIO has no fileno, so it tests a different codepath
71
88
        output = StringIO()
72
89
    else:
73
 
        output = tempfile.TemporaryFile()
 
90
        output = TemporaryFile()
74
91
    try:
75
 
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
76
 
    except errors.NoDiff:
77
 
        raise tests.TestSkipped('external "diff" not present to test')
 
92
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
93
    except NoDiff:
 
94
        raise TestSkipped('external "diff" not present to test')
78
95
    output.seek(0, 0)
79
96
    lines = output.readlines()
80
97
    output.close()
81
98
    return lines
82
99
 
83
100
 
84
 
class TestDiff(tests.TestCase):
 
101
class TestDiff(TestCase):
85
102
 
86
103
    def test_add_nl(self):
87
104
        """diff generates a valid diff for patches that add a newline"""
123
140
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
141
 
125
142
    def test_binary_lines(self):
126
 
        empty = []
127
 
        uni_lines = [1023 * 'a' + '\x00']
128
 
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
129
 
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
130
 
        udiff_lines(uni_lines , empty, allow_binary=True)
131
 
        udiff_lines(empty, uni_lines, allow_binary=True)
 
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)
132
147
 
133
148
    def test_external_diff(self):
134
149
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
164
179
        orig_path = os.environ['PATH']
165
180
        try:
166
181
            os.environ['PATH'] = ''
167
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
 
182
            self.assertRaises(NoDiff, external_diff,
168
183
                              'old', ['boo\n'], 'new', ['goo\n'],
169
184
                              StringIO(), diff_opts=['-u'])
170
185
        finally:
173
188
    def test_internal_diff_default(self):
174
189
        # Default internal diff encoding is utf8
175
190
        output = StringIO()
176
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
177
 
                           u'new_\xe5', ['new_text\n'], output)
 
191
        internal_diff(u'old_\xb5', ['old_text\n'],
 
192
                    u'new_\xe5', ['new_text\n'], output)
178
193
        lines = output.getvalue().splitlines(True)
179
194
        self.check_patch(lines)
180
195
        self.assertEquals(['--- old_\xc2\xb5\n',
188
203
 
189
204
    def test_internal_diff_utf8(self):
190
205
        output = StringIO()
191
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
192
 
                           u'new_\xe5', ['new_text\n'], output,
193
 
                           path_encoding='utf8')
 
206
        internal_diff(u'old_\xb5', ['old_text\n'],
 
207
                    u'new_\xe5', ['new_text\n'], output,
 
208
                    path_encoding='utf8')
194
209
        lines = output.getvalue().splitlines(True)
195
210
        self.check_patch(lines)
196
211
        self.assertEquals(['--- old_\xc2\xb5\n',
204
219
 
205
220
    def test_internal_diff_iso_8859_1(self):
206
221
        output = StringIO()
207
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
208
 
                           u'new_\xe5', ['new_text\n'], output,
209
 
                           path_encoding='iso-8859-1')
 
222
        internal_diff(u'old_\xb5', ['old_text\n'],
 
223
                    u'new_\xe5', ['new_text\n'], output,
 
224
                    path_encoding='iso-8859-1')
210
225
        lines = output.getvalue().splitlines(True)
211
226
        self.check_patch(lines)
212
227
        self.assertEquals(['--- old_\xb5\n',
220
235
 
221
236
    def test_internal_diff_no_content(self):
222
237
        output = StringIO()
223
 
        diff.internal_diff(u'old', [], u'new', [], output)
 
238
        internal_diff(u'old', [], u'new', [], output)
224
239
        self.assertEqual('', output.getvalue())
225
240
 
226
241
    def test_internal_diff_no_changes(self):
227
242
        output = StringIO()
228
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
229
 
                           u'new', ['text\n', 'contents\n'],
230
 
                           output)
 
243
        internal_diff(u'old', ['text\n', 'contents\n'],
 
244
                      u'new', ['text\n', 'contents\n'],
 
245
                      output)
231
246
        self.assertEqual('', output.getvalue())
232
247
 
233
248
    def test_internal_diff_returns_bytes(self):
234
249
        import StringIO
235
250
        output = StringIO.StringIO()
236
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
237
 
                            u'new_\xe5', ['new_text\n'], output)
 
251
        internal_diff(u'old_\xb5', ['old_text\n'],
 
252
                    u'new_\xe5', ['new_text\n'], output)
238
253
        self.failUnless(isinstance(output.getvalue(), str),
239
254
            'internal_diff should return bytestrings')
240
255
 
241
256
 
242
 
class TestDiffFiles(tests.TestCaseInTempDir):
 
257
class TestDiffFiles(TestCaseInTempDir):
243
258
 
244
259
    def test_external_diff_binary(self):
245
260
        """The output when using external diff should use diff's i18n error"""
258
273
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
259
274
 
260
275
 
261
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
 
276
class TestShowDiffTreesHelper(TestCaseWithTransport):
262
277
    """Has a helper for running show_diff_trees"""
263
278
 
264
279
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
267
282
            extra_trees = (working_tree,)
268
283
        else:
269
284
            extra_trees = ()
270
 
        diff.show_diff_trees(tree1, tree2, output,
271
 
                             specific_files=specific_files,
272
 
                             extra_trees=extra_trees, old_label='old/',
273
 
                             new_label='new/')
 
285
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
 
286
                        extra_trees=extra_trees, old_label='old/',
 
287
                        new_label='new/')
274
288
        return output.getvalue()
275
289
 
276
290
 
415
429
        tree.commit('one', rev_id='rev-1')
416
430
 
417
431
        self.build_tree_contents([('tree/file', 'new contents\n')])
418
 
        d = self.get_diff(tree.basis_tree(), tree)
419
 
        self.assertContainsRe(d, "=== modified file 'file'\n")
420
 
        self.assertContainsRe(d, '--- old/file\t')
421
 
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
422
 
        self.assertContainsRe(d, '-contents\n'
423
 
                                 '\\+new contents\n')
 
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')
424
438
 
425
439
    def test_modified_file_in_renamed_dir(self):
426
440
        """Test when a file is modified in a renamed directory."""
432
446
 
433
447
        tree.rename_one('dir', 'other')
434
448
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
435
 
        d = self.get_diff(tree.basis_tree(), tree)
436
 
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
437
 
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
 
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")
438
452
        # XXX: This is technically incorrect, because it used to be at another
439
453
        # location. What to do?
440
 
        self.assertContainsRe(d, '--- old/dir/file\t')
441
 
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
442
 
        self.assertContainsRe(d, '-contents\n'
443
 
                                 '\\+new contents\n')
 
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')
444
458
 
445
459
    def test_renamed_directory(self):
446
460
        """Test when only a directory is only renamed."""
451
465
        tree.commit('one', rev_id='rev-1')
452
466
 
453
467
        tree.rename_one('dir', 'newdir')
454
 
        d = self.get_diff(tree.basis_tree(), tree)
 
468
        diff = self.get_diff(tree.basis_tree(), tree)
455
469
        # Renaming a directory should be a single "you renamed this dir" even
456
470
        # when there are files inside.
457
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
471
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
458
472
 
459
473
    def test_renamed_file(self):
460
474
        """Test when a file is only renamed."""
464
478
        tree.commit('one', rev_id='rev-1')
465
479
 
466
480
        tree.rename_one('file', 'newname')
467
 
        d = self.get_diff(tree.basis_tree(), tree)
468
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
481
        diff = self.get_diff(tree.basis_tree(), tree)
 
482
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
469
483
        # We shouldn't have a --- or +++ line, because there is no content
470
484
        # change
471
 
        self.assertNotContainsRe(d, '---')
 
485
        self.assertNotContainsRe(diff, '---')
472
486
 
473
487
    def test_renamed_and_modified_file(self):
474
488
        """Test when a file is only renamed."""
479
493
 
480
494
        tree.rename_one('file', 'newname')
481
495
        self.build_tree_contents([('tree/newname', 'new contents\n')])
482
 
        d = self.get_diff(tree.basis_tree(), tree)
483
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
484
 
        self.assertContainsRe(d, '--- old/file\t')
485
 
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
486
 
        self.assertContainsRe(d, '-contents\n'
487
 
                                 '\\+new contents\n')
 
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')
488
502
 
489
503
 
490
504
    def test_internal_diff_exec_property(self):
509
523
        tree.rename_one('c', 'new-c')
510
524
        tree.rename_one('d', 'new-d')
511
525
 
512
 
        d = self.get_diff(tree.basis_tree(), tree)
513
 
 
514
 
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
515
 
                                  ".*\+x to -x.*\)")
516
 
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
517
 
                                  ".*-x to \+x.*\)")
518
 
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
519
 
                                  ".*\+x to -x.*\)")
520
 
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
521
 
                                  ".*-x to \+x.*\)")
522
 
        self.assertNotContainsRe(d, r"file 'e'")
523
 
        self.assertNotContainsRe(d, r"file 'f'")
 
526
        diff = self.get_diff(tree.basis_tree(), tree)
 
527
 
 
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'")
 
534
 
524
535
 
525
536
    def test_binary_unicode_filenames(self):
526
537
        """Test that contents of files are *not* encoded in UTF-8 when there
541
552
        tree.add([alpha], ['file-id'])
542
553
        tree.add([omega], ['file-id-2'])
543
554
        diff_content = StringIO()
544
 
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
545
 
        d = diff_content.getvalue()
546
 
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
547
 
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
548
 
                              % (alpha_utf8, alpha_utf8))
549
 
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
550
 
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
551
 
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
 
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,))
552
563
 
553
564
    def test_unicode_filename(self):
554
565
        """Test when the filename are unicode."""
573
584
        tree.add(['add_'+alpha], ['file-id'])
574
585
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
575
586
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
577
 
        self.assertContainsRe(d,
 
587
        diff = self.get_diff(tree.basis_tree(), tree)
 
588
        self.assertContainsRe(diff,
578
589
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
 
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
 
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
 
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
582
 
 
583
 
    def test_unicode_filename_path_encoding(self):
584
 
        """Test for bug #382699: unicode filenames on Windows should be shown
585
 
        in user encoding.
586
 
        """
587
 
        self.requireFeature(tests.UnicodeFilenameFeature)
588
 
        # The word 'test' in Russian
589
 
        _russian_test = u'\u0422\u0435\u0441\u0442'
590
 
        directory = _russian_test + u'/'
591
 
        test_txt = _russian_test + u'.txt'
592
 
        u1234 = u'\u1234.txt'
593
 
 
594
 
        tree = self.make_branch_and_tree('.')
595
 
        self.build_tree_contents([
596
 
            (test_txt, 'foo\n'),
597
 
            (u1234, 'foo\n'),
598
 
            (directory, None),
599
 
            ])
600
 
        tree.add([test_txt, u1234, directory])
601
 
 
602
 
        sio = StringIO()
603
 
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
604
 
            path_encoding='cp1251')
605
 
 
606
 
        output = subst_dates(sio.getvalue())
607
 
        shouldbe = ('''\
608
 
=== added directory '%(directory)s'
609
 
=== added file '%(test_txt)s'
610
 
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
611
 
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
612
 
@@ -0,0 +1,1 @@
613
 
+foo
614
 
 
615
 
=== added file '?.txt'
616
 
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
617
 
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
618
 
@@ -0,0 +1,1 @@
619
 
+foo
620
 
 
621
 
''' % {'directory': _russian_test.encode('cp1251'),
622
 
       'test_txt': test_txt.encode('cp1251'),
623
 
      })
624
 
        self.assertEqualDiff(output, shouldbe)
625
 
 
626
 
 
627
 
class DiffWasIs(diff.DiffPath):
 
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):
628
596
 
629
597
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
630
598
        self.to_file.write('was: ')
634
602
        pass
635
603
 
636
604
 
637
 
class TestDiffTree(tests.TestCaseWithTransport):
 
605
class TestDiffTree(TestCaseWithTransport):
638
606
 
639
607
    def setUp(self):
640
 
        super(TestDiffTree, self).setUp()
 
608
        TestCaseWithTransport.setUp(self)
641
609
        self.old_tree = self.make_branch_and_tree('old-tree')
642
610
        self.old_tree.lock_write()
643
611
        self.addCleanup(self.old_tree.unlock)
644
612
        self.new_tree = self.make_branch_and_tree('new-tree')
645
613
        self.new_tree.lock_write()
646
614
        self.addCleanup(self.new_tree.unlock)
647
 
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
615
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
648
616
 
649
617
    def test_diff_text(self):
650
618
        self.build_tree_contents([('old-tree/olddir/',),
655
623
                                  ('new-tree/newdir/newfile', 'new\n')])
656
624
        self.new_tree.add('newdir')
657
625
        self.new_tree.add('newdir/newfile', 'file-id')
658
 
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
 
626
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
659
627
        differ.diff_text('file-id', None, 'old label', 'new label')
660
628
        self.assertEqual(
661
629
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
690
658
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
691
659
 
692
660
    def test_diff_symlink(self):
693
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
661
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
694
662
        differ.diff_symlink('old target', None)
695
663
        self.assertEqual("=== target was 'old target'\n",
696
664
                         differ.to_file.getvalue())
697
665
 
698
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
666
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
699
667
        differ.diff_symlink(None, 'new target')
700
668
        self.assertEqual("=== target is 'new target'\n",
701
669
                         differ.to_file.getvalue())
702
670
 
703
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
671
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
704
672
        differ.diff_symlink('old target', 'new target')
705
673
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
706
674
                         differ.to_file.getvalue())
756
724
 
757
725
    def test_register_diff(self):
758
726
        self.create_old_new()
759
 
        old_diff_factories = diff.DiffTree.diff_factories
760
 
        diff.DiffTree.diff_factories=old_diff_factories[:]
761
 
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
727
        old_diff_factories = DiffTree.diff_factories
 
728
        DiffTree.diff_factories=old_diff_factories[:]
 
729
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
762
730
        try:
763
 
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
731
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
764
732
        finally:
765
 
            diff.DiffTree.diff_factories = old_diff_factories
 
733
            DiffTree.diff_factories = old_diff_factories
766
734
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
767
735
        self.assertNotContainsRe(
768
736
            differ.to_file.getvalue(),
773
741
 
774
742
    def test_extra_factories(self):
775
743
        self.create_old_new()
776
 
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
777
 
                               extra_factories=[DiffWasIs.from_diff_tree])
 
744
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
 
745
                            extra_factories=[DiffWasIs.from_diff_tree])
778
746
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
779
747
        self.assertNotContainsRe(
780
748
            differ.to_file.getvalue(),
793
761
            '.*a-file(.|\n)*b-file')
794
762
 
795
763
 
796
 
class TestPatienceDiffLib(tests.TestCase):
 
764
class TestPatienceDiffLib(TestCase):
797
765
 
798
766
    def setUp(self):
799
767
        super(TestPatienceDiffLib, self).setUp()
800
 
        self._unique_lcs = _patiencediff_py.unique_lcs_py
801
 
        self._recurse_matches = _patiencediff_py.recurse_matches_py
 
768
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
 
769
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
802
770
        self._PatienceSequenceMatcher = \
803
 
            _patiencediff_py.PatienceSequenceMatcher_py
 
771
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
804
772
 
805
773
    def test_diff_unicode_string(self):
806
774
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
1113
1081
                 'how are you today?\n']
1114
1082
        txt_b = ['hello there\n',
1115
1083
                 'how are you today?\n']
1116
 
        unified_diff = patiencediff.unified_diff
 
1084
        unified_diff = bzrlib.patiencediff.unified_diff
1117
1085
        psm = self._PatienceSequenceMatcher
1118
1086
        self.assertEquals(['--- \n',
1119
1087
                           '+++ \n',
1167
1135
                 'how are you today?\n']
1168
1136
        txt_b = ['hello there\n',
1169
1137
                 'how are you today?\n']
1170
 
        unified_diff = patiencediff.unified_diff
 
1138
        unified_diff = bzrlib.patiencediff.unified_diff
1171
1139
        psm = self._PatienceSequenceMatcher
1172
1140
        self.assertEquals(['--- a\t2008-08-08\n',
1173
1141
                           '+++ b\t2008-09-09\n',
1185
1153
 
1186
1154
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1187
1155
 
1188
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1156
    _test_needs_features = [CompiledPatienceDiffFeature]
1189
1157
 
1190
1158
    def setUp(self):
1191
1159
        super(TestPatienceDiffLib_c, self).setUp()
1192
 
        from bzrlib import _patiencediff_c
1193
 
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1194
 
        self._recurse_matches = _patiencediff_c.recurse_matches_c
 
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
1195
1163
        self._PatienceSequenceMatcher = \
1196
 
            _patiencediff_c.PatienceSequenceMatcher_c
 
1164
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1197
1165
 
1198
1166
    def test_unhashable(self):
1199
1167
        """We should get a proper exception here."""
1209
1177
                                         None, ['valid'], ['valid', []])
1210
1178
 
1211
1179
 
1212
 
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1180
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1213
1181
 
1214
1182
    def setUp(self):
1215
1183
        super(TestPatienceDiffLibFiles, self).setUp()
1216
1184
        self._PatienceSequenceMatcher = \
1217
 
            _patiencediff_py.PatienceSequenceMatcher_py
 
1185
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1218
1186
 
1219
1187
    def test_patience_unified_diff_files(self):
1220
1188
        txt_a = ['hello there\n',
1225
1193
        open('a1', 'wb').writelines(txt_a)
1226
1194
        open('b1', 'wb').writelines(txt_b)
1227
1195
 
1228
 
        unified_diff_files = patiencediff.unified_diff_files
 
1196
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1229
1197
        psm = self._PatienceSequenceMatcher
1230
1198
        self.assertEquals(['--- a1\n',
1231
1199
                           '+++ b1\n',
1281
1249
 
1282
1250
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1283
1251
 
1284
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1252
    _test_needs_features = [CompiledPatienceDiffFeature]
1285
1253
 
1286
1254
    def setUp(self):
1287
1255
        super(TestPatienceDiffLibFiles_c, self).setUp()
1288
 
        from bzrlib import _patiencediff_c
 
1256
        import bzrlib._patiencediff_c
1289
1257
        self._PatienceSequenceMatcher = \
1290
 
            _patiencediff_c.PatienceSequenceMatcher_c
1291
 
 
1292
 
 
1293
 
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1258
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1259
 
 
1260
 
 
1261
class TestUsingCompiledIfAvailable(TestCase):
1294
1262
 
1295
1263
    def test_PatienceSequenceMatcher(self):
1296
 
        if compiled_patiencediff_feature.available():
 
1264
        if CompiledPatienceDiffFeature.available():
1297
1265
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1298
1266
            self.assertIs(PatienceSequenceMatcher_c,
1299
 
                          patiencediff.PatienceSequenceMatcher)
 
1267
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1300
1268
        else:
1301
1269
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1302
1270
            self.assertIs(PatienceSequenceMatcher_py,
1303
 
                          patiencediff.PatienceSequenceMatcher)
 
1271
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1304
1272
 
1305
1273
    def test_unique_lcs(self):
1306
 
        if compiled_patiencediff_feature.available():
 
1274
        if CompiledPatienceDiffFeature.available():
1307
1275
            from bzrlib._patiencediff_c import unique_lcs_c
1308
1276
            self.assertIs(unique_lcs_c,
1309
 
                          patiencediff.unique_lcs)
 
1277
                          bzrlib.patiencediff.unique_lcs)
1310
1278
        else:
1311
1279
            from bzrlib._patiencediff_py import unique_lcs_py
1312
1280
            self.assertIs(unique_lcs_py,
1313
 
                          patiencediff.unique_lcs)
 
1281
                          bzrlib.patiencediff.unique_lcs)
1314
1282
 
1315
1283
    def test_recurse_matches(self):
1316
 
        if compiled_patiencediff_feature.available():
 
1284
        if CompiledPatienceDiffFeature.available():
1317
1285
            from bzrlib._patiencediff_c import recurse_matches_c
1318
1286
            self.assertIs(recurse_matches_c,
1319
 
                          patiencediff.recurse_matches)
 
1287
                          bzrlib.patiencediff.recurse_matches)
1320
1288
        else:
1321
1289
            from bzrlib._patiencediff_py import recurse_matches_py
1322
1290
            self.assertIs(recurse_matches_py,
1323
 
                          patiencediff.recurse_matches)
1324
 
 
1325
 
 
1326
 
class TestDiffFromTool(tests.TestCaseWithTransport):
 
1291
                          bzrlib.patiencediff.recurse_matches)
 
1292
 
 
1293
 
 
1294
class TestDiffFromTool(TestCaseWithTransport):
1327
1295
 
1328
1296
    def test_from_string(self):
1329
 
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
 
1297
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1330
1298
        self.addCleanup(diff_obj.finish)
1331
 
        self.assertEqual(['diff', '@old_path', '@new_path'],
 
1299
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1332
1300
            diff_obj.command_template)
1333
1301
 
1334
1302
    def test_from_string_u5(self):
1335
 
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1336
 
                                                 None, None, None)
 
1303
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1337
1304
        self.addCleanup(diff_obj.finish)
1338
 
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
 
1305
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1339
1306
                         diff_obj.command_template)
1340
1307
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1341
1308
                         diff_obj._get_command('old-path', 'new-path'))
1342
1309
 
1343
 
    def test_from_string_path_with_backslashes(self):
1344
 
        self.requireFeature(features.backslashdir_feature)
1345
 
        tool = 'C:\\Tools\\Diff.exe'
1346
 
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1347
 
        self.addCleanup(diff_obj.finish)
1348
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
1349
 
                         diff_obj.command_template)
1350
 
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1351
 
                         diff_obj._get_command('old-path', 'new-path'))
1352
 
 
1353
1310
    def test_execute(self):
1354
1311
        output = StringIO()
1355
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1356
 
                                      'print "@old_path @new_path"'],
1357
 
                                     None, None, output)
 
1312
        diff_obj = DiffFromTool(['python', '-c',
 
1313
                                 'print "%(old_path)s %(new_path)s"'],
 
1314
                                None, None, output)
1358
1315
        self.addCleanup(diff_obj.finish)
1359
1316
        diff_obj._execute('old', 'new')
1360
1317
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1361
1318
 
1362
1319
    def test_excute_missing(self):
1363
 
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1364
 
                                     None, None, None)
 
1320
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1321
                                None, None, None)
1365
1322
        self.addCleanup(diff_obj.finish)
1366
 
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1367
 
                              'old', 'new')
 
1323
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
 
1324
                              'new')
1368
1325
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1369
1326
                         ' on this machine', str(e))
1370
1327
 
1377
1334
        tree.commit('old tree')
1378
1335
        tree.lock_read()
1379
1336
        self.addCleanup(tree.unlock)
1380
 
        basis_tree = tree.basis_tree()
1381
 
        basis_tree.lock_read()
1382
 
        self.addCleanup(basis_tree.unlock)
1383
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1384
 
                                      'print "@old_path @new_path"'],
1385
 
                                     basis_tree, tree, output)
 
1337
        diff_obj = DiffFromTool(['python', '-c',
 
1338
                                 'print "%(old_path)s %(new_path)s"'],
 
1339
                                tree, tree, output)
1386
1340
        diff_obj._prepare_files('file-id', 'file', 'file')
1387
 
        # The old content should be readonly
1388
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1389
 
                                    r'R.*old\\file$')
1390
 
        # The new content should use the tree object, not a 'new' file anymore
1391
 
        self.assertEndsWith(tree.basedir, 'work/tree')
1392
 
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
 
1341
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
 
1342
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
1393
1343
 
1394
1344
    def assertReadableByAttrib(self, cwd, relpath, regex):
1395
1345
        proc = subprocess.Popen(['attrib', relpath],
1396
1346
                                stdout=subprocess.PIPE,
1397
1347
                                cwd=cwd)
1398
 
        (result, err) = proc.communicate()
1399
 
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
 
1348
        proc.wait()
 
1349
        result = proc.stdout.read()
 
1350
        self.assertContainsRe(result, regex)
1400
1351
 
1401
1352
    def test_prepare_files(self):
1402
1353
        output = StringIO()
1405
1356
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1406
1357
        tree.add('oldname', 'file-id')
1407
1358
        tree.add('oldname2', 'file2-id')
1408
 
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1409
 
        tree.commit('old tree', timestamp=315532800)
 
1359
        tree.commit('old tree', timestamp=0)
1410
1360
        tree.rename_one('oldname', 'newname')
1411
1361
        tree.rename_one('oldname2', 'newname2')
1412
1362
        self.build_tree_contents([('tree/newname', 'newcontent')])
1416
1366
        self.addCleanup(old_tree.unlock)
1417
1367
        tree.lock_read()
1418
1368
        self.addCleanup(tree.unlock)
1419
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1420
 
                                      'print "@old_path @new_path"'],
1421
 
                                     old_tree, tree, output)
 
1369
        diff_obj = DiffFromTool(['python', '-c',
 
1370
                                 'print "%(old_path)s %(new_path)s"'],
 
1371
                                old_tree, tree, output)
1422
1372
        self.addCleanup(diff_obj.finish)
1423
1373
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1424
1374
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1425
1375
                                                     'newname')
1426
1376
        self.assertContainsRe(old_path, 'old/oldname$')
1427
 
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1428
 
        self.assertContainsRe(new_path, 'tree/newname$')
 
1377
        self.assertEqual(0, os.stat(old_path).st_mtime)
 
1378
        self.assertContainsRe(new_path, 'new/newname$')
1429
1379
        self.assertFileEqual('oldcontent', old_path)
1430
1380
        self.assertFileEqual('newcontent', new_path)
1431
1381
        if osutils.host_os_dereferences_symlinks():
1432
1382
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1433
1383
        # make sure we can create files with the same parent directories
1434
1384
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1435
 
 
1436
 
 
1437
 
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1438
 
 
1439
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1440
 
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
1441
 
        TestGetTreesAndBranchesToDiff.
1442
 
        """
1443
 
        return diff.get_trees_and_branches_to_diff_locked(
1444
 
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1445
 
 
1446
 
    def test_basic(self):
1447
 
        tree = self.make_branch_and_tree('tree')
1448
 
        (old_tree, new_tree,
1449
 
         old_branch, new_branch,
1450
 
         specific_files, extra_trees) = self.call_gtabtd(
1451
 
             ['tree'], None, None, None)
1452
 
 
1453
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1454
 
        self.assertEqual(_mod_revision.NULL_REVISION,
1455
 
                         old_tree.get_revision_id())
1456
 
        self.assertEqual(tree.basedir, new_tree.basedir)
1457
 
        self.assertEqual(tree.branch.base, old_branch.base)
1458
 
        self.assertEqual(tree.branch.base, new_branch.base)
1459
 
        self.assertIs(None, specific_files)
1460
 
        self.assertIs(None, extra_trees)
1461
 
 
1462
 
    def test_with_rev_specs(self):
1463
 
        tree = self.make_branch_and_tree('tree')
1464
 
        self.build_tree_contents([('tree/file', 'oldcontent')])
1465
 
        tree.add('file', 'file-id')
1466
 
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1467
 
        self.build_tree_contents([('tree/file', 'newcontent')])
1468
 
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1469
 
 
1470
 
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1471
 
                     revisionspec.RevisionSpec.from_string('2')]
1472
 
        (old_tree, new_tree,
1473
 
         old_branch, new_branch,
1474
 
         specific_files, extra_trees) = self.call_gtabtd(
1475
 
            ['tree'], revisions, None, None)
1476
 
 
1477
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1478
 
        self.assertEqual("old-id", old_tree.get_revision_id())
1479
 
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1480
 
        self.assertEqual("new-id", new_tree.get_revision_id())
1481
 
        self.assertEqual(tree.branch.base, old_branch.base)
1482
 
        self.assertEqual(tree.branch.base, new_branch.base)
1483
 
        self.assertIs(None, specific_files)
1484
 
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
1485
 
 
1486
 
 
1487
 
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
1488
 
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
1489
 
    deprecated get_trees_and_branches_to_diff function.
1490
 
    """
1491
 
 
1492
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1493
 
        return self.applyDeprecated(
1494
 
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
1495
 
            path_list, revision_specs, old_url, new_url)
1496