~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Ian Clatworthy
  • Date: 2007-12-11 02:07:30 UTC
  • mto: (3119.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3120.
  • Revision ID: ian.clatworthy@internode.on.net-20071211020730-sdj4kj794dw0628e
make help topics more discoverable

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import os
18
18
from cStringIO import StringIO
 
19
import errno
19
20
import subprocess
20
 
import sys
21
 
import tempfile
 
21
from tempfile import TemporaryFile
22
22
 
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,
 
23
from bzrlib.diff import (
 
24
    internal_diff,
 
25
    external_diff,
 
26
    DiffPath,
 
27
    show_diff_trees,
 
28
    DiffSymlink,
 
29
    DiffTree,
 
30
    DiffText,
34
31
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import test_win32utils
37
 
 
38
 
 
39
 
class _AttribFeature(tests.Feature):
 
32
from bzrlib.errors import BinaryFile, NoDiff
 
33
import bzrlib.osutils as osutils
 
34
import bzrlib.patiencediff
 
35
import bzrlib._patiencediff_py
 
36
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
 
37
                          TestCaseInTempDir, TestSkipped)
 
38
 
 
39
 
 
40
class _CompiledPatienceDiffFeature(Feature):
40
41
 
41
42
    def _probe(self):
42
 
        if (sys.platform not in ('cygwin', 'win32')):
43
 
            return False
44
43
        try:
45
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
46
 
        except OSError, e:
 
44
            import bzrlib._patiencediff_c
 
45
        except ImportError:
47
46
            return False
48
 
        return (0 == proc.wait())
 
47
        return True
49
48
 
50
49
    def feature_name(self):
51
 
        return 'attrib Windows command-line tool'
52
 
 
53
 
AttribFeature = _AttribFeature()
54
 
 
55
 
 
56
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
57
 
                                    'bzrlib._patiencediff_c')
58
 
 
 
50
        return 'bzrlib._patiencediff_c'
 
51
 
 
52
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
53
 
 
54
 
 
55
class _UnicodeFilename(Feature):
 
56
    """Does the filesystem support Unicode filenames?"""
 
57
 
 
58
    def _probe(self):
 
59
        try:
 
60
            os.stat(u'\u03b1')
 
61
        except UnicodeEncodeError:
 
62
            return False
 
63
        except (IOError, OSError):
 
64
            # The filesystem allows the Unicode filename but the file doesn't
 
65
            # exist.
 
66
            return True
 
67
        else:
 
68
            # The filesystem allows the Unicode filename and the file exists,
 
69
            # for some reason.
 
70
            return True
 
71
 
 
72
UnicodeFilename = _UnicodeFilename()
 
73
 
 
74
 
 
75
class TestUnicodeFilename(TestCase):
 
76
 
 
77
    def test_probe_passes(self):
 
78
        """UnicodeFilename._probe passes."""
 
79
        # We can't test much more than that because the behaviour depends
 
80
        # on the platform.
 
81
        UnicodeFilename._probe()
 
82
        
59
83
 
60
84
def udiff_lines(old, new, allow_binary=False):
61
85
    output = StringIO()
62
 
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
 
86
    internal_diff('old', old, 'new', new, output, allow_binary)
63
87
    output.seek(0, 0)
64
88
    return output.readlines()
65
89
 
69
93
        # StringIO has no fileno, so it tests a different codepath
70
94
        output = StringIO()
71
95
    else:
72
 
        output = tempfile.TemporaryFile()
 
96
        output = TemporaryFile()
73
97
    try:
74
 
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
75
 
    except errors.NoDiff:
76
 
        raise tests.TestSkipped('external "diff" not present to test')
 
98
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
99
    except NoDiff:
 
100
        raise TestSkipped('external "diff" not present to test')
77
101
    output.seek(0, 0)
78
102
    lines = output.readlines()
79
103
    output.close()
80
104
    return lines
81
105
 
82
106
 
83
 
class TestDiff(tests.TestCase):
 
107
class TestDiff(TestCase):
84
108
 
85
109
    def test_add_nl(self):
86
110
        """diff generates a valid diff for patches that add a newline"""
122
146
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
123
147
 
124
148
    def test_binary_lines(self):
125
 
        empty = []
126
 
        uni_lines = [1023 * 'a' + '\x00']
127
 
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
128
 
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
 
        udiff_lines(uni_lines , empty, allow_binary=True)
130
 
        udiff_lines(empty, uni_lines, allow_binary=True)
 
149
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
 
150
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
 
151
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
 
152
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
131
153
 
132
154
    def test_external_diff(self):
133
155
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
163
185
        orig_path = os.environ['PATH']
164
186
        try:
165
187
            os.environ['PATH'] = ''
166
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
 
188
            self.assertRaises(NoDiff, external_diff,
167
189
                              'old', ['boo\n'], 'new', ['goo\n'],
168
190
                              StringIO(), diff_opts=['-u'])
169
191
        finally:
170
192
            os.environ['PATH'] = orig_path
171
 
 
 
193
        
172
194
    def test_internal_diff_default(self):
173
195
        # Default internal diff encoding is utf8
174
196
        output = StringIO()
175
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
 
                           u'new_\xe5', ['new_text\n'], output)
 
197
        internal_diff(u'old_\xb5', ['old_text\n'],
 
198
                    u'new_\xe5', ['new_text\n'], output)
177
199
        lines = output.getvalue().splitlines(True)
178
200
        self.check_patch(lines)
179
201
        self.assertEquals(['--- old_\xc2\xb5\n',
187
209
 
188
210
    def test_internal_diff_utf8(self):
189
211
        output = StringIO()
190
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
 
                           u'new_\xe5', ['new_text\n'], output,
192
 
                           path_encoding='utf8')
 
212
        internal_diff(u'old_\xb5', ['old_text\n'],
 
213
                    u'new_\xe5', ['new_text\n'], output,
 
214
                    path_encoding='utf8')
193
215
        lines = output.getvalue().splitlines(True)
194
216
        self.check_patch(lines)
195
217
        self.assertEquals(['--- old_\xc2\xb5\n',
203
225
 
204
226
    def test_internal_diff_iso_8859_1(self):
205
227
        output = StringIO()
206
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
207
 
                           u'new_\xe5', ['new_text\n'], output,
208
 
                           path_encoding='iso-8859-1')
 
228
        internal_diff(u'old_\xb5', ['old_text\n'],
 
229
                    u'new_\xe5', ['new_text\n'], output,
 
230
                    path_encoding='iso-8859-1')
209
231
        lines = output.getvalue().splitlines(True)
210
232
        self.check_patch(lines)
211
233
        self.assertEquals(['--- old_\xb5\n',
219
241
 
220
242
    def test_internal_diff_no_content(self):
221
243
        output = StringIO()
222
 
        diff.internal_diff(u'old', [], u'new', [], output)
 
244
        internal_diff(u'old', [], u'new', [], output)
223
245
        self.assertEqual('', output.getvalue())
224
246
 
225
247
    def test_internal_diff_no_changes(self):
226
248
        output = StringIO()
227
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
 
                           u'new', ['text\n', 'contents\n'],
229
 
                           output)
 
249
        internal_diff(u'old', ['text\n', 'contents\n'],
 
250
                      u'new', ['text\n', 'contents\n'],
 
251
                      output)
230
252
        self.assertEqual('', output.getvalue())
231
253
 
232
254
    def test_internal_diff_returns_bytes(self):
233
255
        import StringIO
234
256
        output = StringIO.StringIO()
235
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
 
                            u'new_\xe5', ['new_text\n'], output)
 
257
        internal_diff(u'old_\xb5', ['old_text\n'],
 
258
                    u'new_\xe5', ['new_text\n'], output)
237
259
        self.failUnless(isinstance(output.getvalue(), str),
238
260
            'internal_diff should return bytestrings')
239
261
 
240
262
 
241
 
class TestDiffFiles(tests.TestCaseInTempDir):
 
263
class TestDiffFiles(TestCaseInTempDir):
242
264
 
243
265
    def test_external_diff_binary(self):
244
266
        """The output when using external diff should use diff's i18n error"""
257
279
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
258
280
 
259
281
 
260
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
 
282
class TestShowDiffTreesHelper(TestCaseWithTransport):
261
283
    """Has a helper for running show_diff_trees"""
262
284
 
263
285
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
266
288
            extra_trees = (working_tree,)
267
289
        else:
268
290
            extra_trees = ()
269
 
        diff.show_diff_trees(tree1, tree2, output,
270
 
                             specific_files=specific_files,
271
 
                             extra_trees=extra_trees, old_label='old/',
272
 
                             new_label='new/')
 
291
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
 
292
                        extra_trees=extra_trees, old_label='old/',
 
293
                        new_label='new/')
273
294
        return output.getvalue()
274
295
 
275
296
 
340
361
+file2 contents at rev 3
341
362
 
342
363
''')
343
 
 
 
364
        
344
365
    def test_diff_add_files(self):
345
 
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
 
366
        tree1 = self.b.repository.revision_tree(None)
346
367
        tree2 = self.b.repository.revision_tree('rev-1')
347
368
        output = self.get_diff(tree1, tree2)
348
369
        # the files have the epoch time stamp for the tree in which
382
403
        self.wt.rename_one('file1', 'file1b')
383
404
        old_tree = self.b.repository.revision_tree('rev-1')
384
405
        new_tree = self.b.repository.revision_tree('rev-4')
385
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
406
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
386
407
                            working_tree=self.wt)
387
408
        self.assertContainsRe(out, 'file1\t')
388
409
 
394
415
        self.wt.rename_one('file1', 'dir1/file1')
395
416
        old_tree = self.b.repository.revision_tree('rev-1')
396
417
        new_tree = self.b.repository.revision_tree('rev-4')
397
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
418
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
398
419
                            working_tree=self.wt)
399
420
        self.assertContainsRe(out, 'file1\t')
400
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
421
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
401
422
                            working_tree=self.wt)
402
423
        self.assertNotContainsRe(out, 'file1\t')
403
424
 
414
435
        tree.commit('one', rev_id='rev-1')
415
436
 
416
437
        self.build_tree_contents([('tree/file', 'new contents\n')])
417
 
        d = self.get_diff(tree.basis_tree(), tree)
418
 
        self.assertContainsRe(d, "=== modified file 'file'\n")
419
 
        self.assertContainsRe(d, '--- old/file\t')
420
 
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
421
 
        self.assertContainsRe(d, '-contents\n'
422
 
                                 '\\+new contents\n')
 
438
        diff = self.get_diff(tree.basis_tree(), tree)
 
439
        self.assertContainsRe(diff, "=== modified file 'file'\n")
 
440
        self.assertContainsRe(diff, '--- old/file\t')
 
441
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
 
442
        self.assertContainsRe(diff, '-contents\n'
 
443
                                    '\\+new contents\n')
423
444
 
424
445
    def test_modified_file_in_renamed_dir(self):
425
446
        """Test when a file is modified in a renamed directory."""
431
452
 
432
453
        tree.rename_one('dir', 'other')
433
454
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
434
 
        d = self.get_diff(tree.basis_tree(), tree)
435
 
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
436
 
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
 
455
        diff = self.get_diff(tree.basis_tree(), tree)
 
456
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
 
457
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
437
458
        # XXX: This is technically incorrect, because it used to be at another
438
459
        # location. What to do?
439
 
        self.assertContainsRe(d, '--- old/dir/file\t')
440
 
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
441
 
        self.assertContainsRe(d, '-contents\n'
442
 
                                 '\\+new contents\n')
 
460
        self.assertContainsRe(diff, '--- old/dir/file\t')
 
461
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
 
462
        self.assertContainsRe(diff, '-contents\n'
 
463
                                    '\\+new contents\n')
443
464
 
444
465
    def test_renamed_directory(self):
445
466
        """Test when only a directory is only renamed."""
450
471
        tree.commit('one', rev_id='rev-1')
451
472
 
452
473
        tree.rename_one('dir', 'newdir')
453
 
        d = self.get_diff(tree.basis_tree(), tree)
 
474
        diff = self.get_diff(tree.basis_tree(), tree)
454
475
        # Renaming a directory should be a single "you renamed this dir" even
455
476
        # when there are files inside.
456
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
477
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
457
478
 
458
479
    def test_renamed_file(self):
459
480
        """Test when a file is only renamed."""
463
484
        tree.commit('one', rev_id='rev-1')
464
485
 
465
486
        tree.rename_one('file', 'newname')
466
 
        d = self.get_diff(tree.basis_tree(), tree)
467
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
487
        diff = self.get_diff(tree.basis_tree(), tree)
 
488
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
468
489
        # We shouldn't have a --- or +++ line, because there is no content
469
490
        # change
470
 
        self.assertNotContainsRe(d, '---')
 
491
        self.assertNotContainsRe(diff, '---')
471
492
 
472
493
    def test_renamed_and_modified_file(self):
473
494
        """Test when a file is only renamed."""
478
499
 
479
500
        tree.rename_one('file', 'newname')
480
501
        self.build_tree_contents([('tree/newname', 'new contents\n')])
481
 
        d = self.get_diff(tree.basis_tree(), tree)
482
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
 
        self.assertContainsRe(d, '--- old/file\t')
484
 
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
485
 
        self.assertContainsRe(d, '-contents\n'
486
 
                                 '\\+new contents\n')
487
 
 
488
 
 
489
 
    def test_internal_diff_exec_property(self):
490
 
        tree = self.make_branch_and_tree('tree')
491
 
 
492
 
        tt = transform.TreeTransform(tree)
493
 
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
494
 
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
495
 
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
496
 
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
497
 
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
498
 
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
499
 
        tt.apply()
500
 
        tree.commit('one', rev_id='rev-1')
501
 
 
502
 
        tt = transform.TreeTransform(tree)
503
 
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
504
 
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
505
 
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
506
 
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
507
 
        tt.apply()
508
 
        tree.rename_one('c', 'new-c')
509
 
        tree.rename_one('d', 'new-d')
510
 
 
511
 
        d = self.get_diff(tree.basis_tree(), tree)
512
 
 
513
 
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
514
 
                                  ".*\+x to -x.*\)")
515
 
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
516
 
                                  ".*-x to \+x.*\)")
517
 
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
518
 
                                  ".*\+x to -x.*\)")
519
 
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
520
 
                                  ".*-x to \+x.*\)")
521
 
        self.assertNotContainsRe(d, r"file 'e'")
522
 
        self.assertNotContainsRe(d, r"file 'f'")
523
 
 
 
502
        diff = self.get_diff(tree.basis_tree(), tree)
 
503
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
504
        self.assertContainsRe(diff, '--- old/file\t')
 
505
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
 
506
        self.assertContainsRe(diff, '-contents\n'
 
507
                                    '\\+new contents\n')
524
508
 
525
509
    def test_binary_unicode_filenames(self):
526
510
        """Test that contents of files are *not* encoded in UTF-8 when there
527
511
        is a binary file in the diff.
528
512
        """
529
513
        # See https://bugs.launchpad.net/bugs/110092.
530
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
514
        self.requireFeature(UnicodeFilename)
531
515
 
532
516
        # This bug isn't triggered with cStringIO.
533
517
        from StringIO import StringIO
541
525
        tree.add([alpha], ['file-id'])
542
526
        tree.add([omega], ['file-id-2'])
543
527
        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,))
 
528
        show_diff_trees(tree.basis_tree(), tree, diff_content)
 
529
        diff = diff_content.getvalue()
 
530
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
 
531
        self.assertContainsRe(
 
532
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
 
533
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
 
534
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
 
535
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
552
536
 
553
537
    def test_unicode_filename(self):
554
538
        """Test when the filename are unicode."""
555
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
539
        self.requireFeature(UnicodeFilename)
556
540
 
557
541
        alpha, omega = u'\u03b1', u'\u03c9'
558
542
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
573
557
        tree.add(['add_'+alpha], ['file-id'])
574
558
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
575
559
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
577
 
        self.assertContainsRe(d,
 
560
        diff = self.get_diff(tree.basis_tree(), tree)
 
561
        self.assertContainsRe(diff,
578
562
                "=== 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
 
 
584
 
class DiffWasIs(diff.DiffPath):
 
563
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
 
564
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
 
565
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
 
566
 
 
567
 
 
568
class DiffWasIs(DiffPath):
585
569
 
586
570
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
587
571
        self.to_file.write('was: ')
591
575
        pass
592
576
 
593
577
 
594
 
class TestDiffTree(tests.TestCaseWithTransport):
 
578
class TestDiffTree(TestCaseWithTransport):
595
579
 
596
580
    def setUp(self):
597
 
        super(TestDiffTree, self).setUp()
 
581
        TestCaseWithTransport.setUp(self)
598
582
        self.old_tree = self.make_branch_and_tree('old-tree')
599
583
        self.old_tree.lock_write()
600
584
        self.addCleanup(self.old_tree.unlock)
601
585
        self.new_tree = self.make_branch_and_tree('new-tree')
602
586
        self.new_tree.lock_write()
603
587
        self.addCleanup(self.new_tree.unlock)
604
 
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
588
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
605
589
 
606
590
    def test_diff_text(self):
607
591
        self.build_tree_contents([('old-tree/olddir/',),
612
596
                                  ('new-tree/newdir/newfile', 'new\n')])
613
597
        self.new_tree.add('newdir')
614
598
        self.new_tree.add('newdir/newfile', 'file-id')
615
 
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
 
599
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
616
600
        differ.diff_text('file-id', None, 'old label', 'new label')
617
601
        self.assertEqual(
618
602
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
647
631
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
648
632
 
649
633
    def test_diff_symlink(self):
650
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
634
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
651
635
        differ.diff_symlink('old target', None)
652
636
        self.assertEqual("=== target was 'old target'\n",
653
637
                         differ.to_file.getvalue())
654
638
 
655
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
639
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
656
640
        differ.diff_symlink(None, 'new target')
657
641
        self.assertEqual("=== target is 'new target'\n",
658
642
                         differ.to_file.getvalue())
659
643
 
660
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
644
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
661
645
        differ.diff_symlink('old target', 'new target')
662
646
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
663
647
                         differ.to_file.getvalue())
678
662
             ' \@\@\n-old\n\+new\n\n')
679
663
 
680
664
    def test_diff_kind_change(self):
681
 
        self.requireFeature(tests.SymlinkFeature)
682
665
        self.build_tree_contents([('old-tree/olddir/',),
683
666
                                  ('old-tree/olddir/oldfile', 'old\n')])
684
667
        self.old_tree.add('olddir')
693
676
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
694
677
             ' \@\@\n-old\n\n')
695
678
        self.assertContainsRe(self.differ.to_file.getvalue(),
696
 
                              "=== target is u'new'\n")
 
679
                              "=== target is 'new'\n")
697
680
 
698
681
    def test_diff_directory(self):
699
682
        self.build_tree(['new-tree/new-dir/'])
713
696
 
714
697
    def test_register_diff(self):
715
698
        self.create_old_new()
716
 
        old_diff_factories = diff.DiffTree.diff_factories
717
 
        diff.DiffTree.diff_factories=old_diff_factories[:]
718
 
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
699
        old_diff_factories = DiffTree.diff_factories
 
700
        DiffTree.diff_factories=old_diff_factories[:]
 
701
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
719
702
        try:
720
 
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
703
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
721
704
        finally:
722
 
            diff.DiffTree.diff_factories = old_diff_factories
 
705
            DiffTree.diff_factories = old_diff_factories
723
706
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
724
707
        self.assertNotContainsRe(
725
708
            differ.to_file.getvalue(),
730
713
 
731
714
    def test_extra_factories(self):
732
715
        self.create_old_new()
733
 
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
734
 
                               extra_factories=[DiffWasIs.from_diff_tree])
 
716
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
 
717
                            extra_factories=[DiffWasIs.from_diff_tree])
735
718
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
736
719
        self.assertNotContainsRe(
737
720
            differ.to_file.getvalue(),
740
723
        self.assertContainsRe(differ.to_file.getvalue(),
741
724
                              'was: old\nis: new\n')
742
725
 
743
 
    def test_alphabetical_order(self):
744
 
        self.build_tree(['new-tree/a-file'])
745
 
        self.new_tree.add('a-file')
746
 
        self.build_tree(['old-tree/b-file'])
747
 
        self.old_tree.add('b-file')
748
 
        self.differ.show_diff(None)
749
 
        self.assertContainsRe(self.differ.to_file.getvalue(),
750
 
            '.*a-file(.|\n)*b-file')
751
 
 
752
 
 
753
 
class TestPatienceDiffLib(tests.TestCase):
 
726
 
 
727
class TestPatienceDiffLib(TestCase):
754
728
 
755
729
    def setUp(self):
756
730
        super(TestPatienceDiffLib, self).setUp()
757
 
        self._unique_lcs = _patiencediff_py.unique_lcs_py
758
 
        self._recurse_matches = _patiencediff_py.recurse_matches_py
 
731
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
 
732
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
759
733
        self._PatienceSequenceMatcher = \
760
 
            _patiencediff_py.PatienceSequenceMatcher_py
761
 
 
762
 
    def test_diff_unicode_string(self):
763
 
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
764
 
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
 
        sm = self._PatienceSequenceMatcher(None, a, b)
766
 
        mb = sm.get_matching_blocks()
767
 
        self.assertEquals(35, len(mb))
 
734
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
768
735
 
769
736
    def test_unique_lcs(self):
770
737
        unique_lcs = self._unique_lcs
776
743
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
777
744
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
778
745
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
779
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
 
746
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
780
747
                                                         (3,3), (4,4)])
781
748
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
782
749
 
797
764
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
798
765
                                          (4, 6), (5, 7), (6, 8)])
799
766
 
800
 
        # recurse_matches doesn't match non-unique
 
767
        # recurse_matches doesn't match non-unique 
801
768
        # lines surrounded by bogus text.
802
769
        # The update has been done in patiencediff.SequenceMatcher instead
803
770
 
807
774
        # This is what it currently gives:
808
775
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
809
776
 
810
 
    def assertDiffBlocks(self, a, b, expected_blocks):
811
 
        """Check that the sequence matcher returns the correct blocks.
812
 
 
813
 
        :param a: A sequence to match
814
 
        :param b: Another sequence to match
815
 
        :param expected_blocks: The expected output, not including the final
816
 
            matching block (len(a), len(b), 0)
817
 
        """
818
 
        matcher = self._PatienceSequenceMatcher(None, a, b)
819
 
        blocks = matcher.get_matching_blocks()
820
 
        last = blocks.pop()
821
 
        self.assertEqual((len(a), len(b), 0), last)
822
 
        self.assertEqual(expected_blocks, blocks)
823
 
 
824
777
    def test_matching_blocks(self):
 
778
        def chk_blocks(a, b, expected_blocks):
 
779
            # difflib always adds a signature of the total
 
780
            # length, with no matching entries at the end
 
781
            s = self._PatienceSequenceMatcher(None, a, b)
 
782
            blocks = s.get_matching_blocks()
 
783
            self.assertEquals((len(a), len(b), 0), blocks[-1])
 
784
            self.assertEquals(expected_blocks, blocks[:-1])
 
785
 
825
786
        # Some basic matching tests
826
 
        self.assertDiffBlocks('', '', [])
827
 
        self.assertDiffBlocks([], [], [])
828
 
        self.assertDiffBlocks('abc', '', [])
829
 
        self.assertDiffBlocks('', 'abc', [])
830
 
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
831
 
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
832
 
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
833
 
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
834
 
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
835
 
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
836
 
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
837
 
        # This may check too much, but it checks to see that
 
787
        chk_blocks('', '', [])
 
788
        chk_blocks([], [], [])
 
789
        chk_blocks('abc', '', [])
 
790
        chk_blocks('', 'abc', [])
 
791
        chk_blocks('abcd', 'abcd', [(0, 0, 4)])
 
792
        chk_blocks('abcd', 'abce', [(0, 0, 3)])
 
793
        chk_blocks('eabc', 'abce', [(1, 0, 3)])
 
794
        chk_blocks('eabce', 'abce', [(1, 0, 4)])
 
795
        chk_blocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
796
        chk_blocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
797
        chk_blocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
798
        # This may check too much, but it checks to see that 
838
799
        # a copied block stays attached to the previous section,
839
800
        # not the later one.
840
801
        # difflib would tend to grab the trailing longest match
841
802
        # which would make the diff not look right
842
 
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
843
 
                              [(0, 0, 6), (6, 11, 10)])
 
803
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
804
                   [(0, 0, 6), (6, 11, 10)])
844
805
 
845
806
        # make sure it supports passing in lists
846
 
        self.assertDiffBlocks(
 
807
        chk_blocks(
847
808
                   ['hello there\n',
848
809
                    'world\n',
849
810
                    'how are you today?\n'],
853
814
 
854
815
        # non unique lines surrounded by non-matching lines
855
816
        # won't be found
856
 
        self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
 
817
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
857
818
 
858
819
        # But they only need to be locally unique
859
 
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
 
820
        chk_blocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
860
821
 
861
822
        # non unique blocks won't be matched
862
 
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
 
823
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
863
824
 
864
825
        # but locally unique ones will
865
 
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
 
826
        chk_blocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
866
827
                                              (5,4,1), (7,5,2), (10,8,1)])
867
828
 
868
 
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
869
 
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
870
 
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
871
 
 
872
 
    def test_matching_blocks_tuples(self):
873
 
        # Some basic matching tests
874
 
        self.assertDiffBlocks([], [], [])
875
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
876
 
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
877
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
878
 
                              [('a',), ('b',), ('c,')],
879
 
                              [(0, 0, 3)])
880
 
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
881
 
                              [('a',), ('b',), ('d,')],
882
 
                              [(0, 0, 2)])
883
 
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
884
 
                              [('a',), ('b',), ('c,')],
885
 
                              [(1, 1, 2)])
886
 
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
887
 
                              [('a',), ('b',), ('c,')],
888
 
                              [(1, 0, 3)])
889
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
890
 
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
891
 
                              [(0, 0, 1), (2, 2, 1)])
892
 
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
893
 
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
894
 
                              [(0, 0, 1), (2, 2, 1)])
 
829
        chk_blocks('abbabbXd', 'cabbabxd', [(7,7,1)])
 
830
        chk_blocks('abbabbbb', 'cabbabbc', [])
 
831
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [])
895
832
 
896
833
    def test_opcodes(self):
897
834
        def chk_ops(a, b, expected_codes):
940
877
                 ('delete', 1,2, 1,1),
941
878
                 ('equal',  2,3, 1,2),
942
879
                ])
943
 
        chk_ops('aBccDe', 'abccde',
 
880
        chk_ops('aBccDe', 'abccde', 
944
881
                [('equal',   0,1, 0,1),
945
882
                 ('replace', 1,5, 1,5),
946
883
                 ('equal',   5,6, 5,6),
947
884
                ])
948
 
        chk_ops('aBcDec', 'abcdec',
 
885
        chk_ops('aBcDec', 'abcdec', 
949
886
                [('equal',   0,1, 0,1),
950
887
                 ('replace', 1,2, 1,2),
951
888
                 ('equal',   2,3, 2,3),
952
889
                 ('replace', 3,4, 3,4),
953
890
                 ('equal',   4,6, 4,6),
954
891
                ])
955
 
        chk_ops('aBcdEcdFg', 'abcdecdfg',
 
892
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
956
893
                [('equal',   0,1, 0,1),
957
894
                 ('replace', 1,8, 1,8),
958
895
                 ('equal',   8,9, 8,9)
959
896
                ])
960
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
 
897
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
961
898
                [('equal',   0,1, 0,1),
962
899
                 ('replace', 1,2, 1,2),
963
900
                 ('equal',   2,4, 2,4),
1009
946
    def test_multiple_ranges(self):
1010
947
        # There was an earlier bug where we used a bad set of ranges,
1011
948
        # this triggers that specific bug, to make sure it doesn't regress
1012
 
        self.assertDiffBlocks('abcdefghijklmnop',
1013
 
                              'abcXghiYZQRSTUVWXYZijklmnop',
1014
 
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1015
 
 
1016
 
        self.assertDiffBlocks('ABCd efghIjk  L',
1017
 
                              'AxyzBCn mo pqrstuvwI1 2  L',
1018
 
                              [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
949
        def chk_blocks(a, b, expected_blocks):
 
950
            # difflib always adds a signature of the total
 
951
            # length, with no matching entries at the end
 
952
            s = self._PatienceSequenceMatcher(None, a, b)
 
953
            blocks = s.get_matching_blocks()
 
954
            x = blocks.pop()
 
955
            self.assertEquals(x, (len(a), len(b), 0))
 
956
            self.assertEquals(expected_blocks, blocks)
 
957
 
 
958
        chk_blocks('abcdefghijklmnop'
 
959
                 , 'abcXghiYZQRSTUVWXYZijklmnop'
 
960
                 , [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
961
 
 
962
        chk_blocks('ABCd efghIjk  L'
 
963
                 , 'AxyzBCn mo pqrstuvwI1 2  L'
 
964
                 , [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1019
965
 
1020
966
        # These are rot13 code snippets.
1021
 
        self.assertDiffBlocks('''\
 
967
        chk_blocks('''\
1022
968
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1023
969
    """
1024
970
    gnxrf_netf = ['svyr*']
1025
971
    gnxrf_bcgvbaf = ['ab-erphefr']
1026
 
 
 
972
  
1027
973
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1028
974
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1029
975
        vs vf_dhvrg():
1037
983
'''.splitlines(True), '''\
1038
984
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1039
985
 
1040
 
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
 
986
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
1041
987
    nqq gurz.
1042
988
    """
1043
989
    gnxrf_netf = ['svyr*']
1070
1016
                 'how are you today?\n']
1071
1017
        txt_b = ['hello there\n',
1072
1018
                 'how are you today?\n']
1073
 
        unified_diff = patiencediff.unified_diff
 
1019
        unified_diff = bzrlib.patiencediff.unified_diff
1074
1020
        psm = self._PatienceSequenceMatcher
1075
 
        self.assertEquals(['--- \n',
1076
 
                           '+++ \n',
 
1021
        self.assertEquals([ '---  \n',
 
1022
                           '+++  \n',
1077
1023
                           '@@ -1,3 +1,2 @@\n',
1078
1024
                           ' hello there\n',
1079
1025
                           '-world\n',
1084
1030
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1085
1031
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1086
1032
        # This is the result with LongestCommonSubstring matching
1087
 
        self.assertEquals(['--- \n',
1088
 
                           '+++ \n',
 
1033
        self.assertEquals(['---  \n',
 
1034
                           '+++  \n',
1089
1035
                           '@@ -1,6 +1,11 @@\n',
1090
1036
                           ' a\n',
1091
1037
                           ' b\n',
1100
1046
                           ' f\n']
1101
1047
                          , list(unified_diff(txt_a, txt_b)))
1102
1048
        # And the patience diff
1103
 
        self.assertEquals(['--- \n',
1104
 
                           '+++ \n',
 
1049
        self.assertEquals(['---  \n',
 
1050
                           '+++  \n',
1105
1051
                           '@@ -4,6 +4,11 @@\n',
1106
1052
                           ' d\n',
1107
1053
                           ' e\n',
1118
1064
                          , list(unified_diff(txt_a, txt_b,
1119
1065
                                 sequencematcher=psm)))
1120
1066
 
1121
 
    def test_patience_unified_diff_with_dates(self):
1122
 
        txt_a = ['hello there\n',
1123
 
                 'world\n',
1124
 
                 'how are you today?\n']
1125
 
        txt_b = ['hello there\n',
1126
 
                 'how are you today?\n']
1127
 
        unified_diff = patiencediff.unified_diff
1128
 
        psm = self._PatienceSequenceMatcher
1129
 
        self.assertEquals(['--- a\t2008-08-08\n',
1130
 
                           '+++ b\t2008-09-09\n',
1131
 
                           '@@ -1,3 +1,2 @@\n',
1132
 
                           ' hello there\n',
1133
 
                           '-world\n',
1134
 
                           ' how are you today?\n'
1135
 
                          ]
1136
 
                          , list(unified_diff(txt_a, txt_b,
1137
 
                                 fromfile='a', tofile='b',
1138
 
                                 fromfiledate='2008-08-08',
1139
 
                                 tofiledate='2008-09-09',
1140
 
                                 sequencematcher=psm)))
1141
 
 
1142
1067
 
1143
1068
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1144
1069
 
1145
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1070
    _test_needs_features = [CompiledPatienceDiffFeature]
1146
1071
 
1147
1072
    def setUp(self):
1148
1073
        super(TestPatienceDiffLib_c, self).setUp()
1149
 
        from bzrlib import _patiencediff_c
1150
 
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1151
 
        self._recurse_matches = _patiencediff_c.recurse_matches_c
 
1074
        import bzrlib._patiencediff_c
 
1075
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
 
1076
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
1152
1077
        self._PatienceSequenceMatcher = \
1153
 
            _patiencediff_c.PatienceSequenceMatcher_c
1154
 
 
1155
 
    def test_unhashable(self):
1156
 
        """We should get a proper exception here."""
1157
 
        # We need to be able to hash items in the sequence, lists are
1158
 
        # unhashable, and thus cannot be diffed
1159
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1160
 
                                         None, [[]], [])
1161
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1162
 
                                         None, ['valid', []], [])
1163
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1164
 
                                         None, ['valid'], [[]])
1165
 
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1166
 
                                         None, ['valid'], ['valid', []])
1167
 
 
1168
 
 
1169
 
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1078
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1079
 
 
1080
 
 
1081
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1170
1082
 
1171
1083
    def setUp(self):
1172
1084
        super(TestPatienceDiffLibFiles, self).setUp()
1173
1085
        self._PatienceSequenceMatcher = \
1174
 
            _patiencediff_py.PatienceSequenceMatcher_py
 
1086
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1175
1087
 
1176
1088
    def test_patience_unified_diff_files(self):
1177
1089
        txt_a = ['hello there\n',
1182
1094
        open('a1', 'wb').writelines(txt_a)
1183
1095
        open('b1', 'wb').writelines(txt_b)
1184
1096
 
1185
 
        unified_diff_files = patiencediff.unified_diff_files
 
1097
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1186
1098
        psm = self._PatienceSequenceMatcher
1187
 
        self.assertEquals(['--- a1\n',
1188
 
                           '+++ b1\n',
 
1099
        self.assertEquals(['--- a1 \n',
 
1100
                           '+++ b1 \n',
1189
1101
                           '@@ -1,3 +1,2 @@\n',
1190
1102
                           ' hello there\n',
1191
1103
                           '-world\n',
1200
1112
        open('b2', 'wb').writelines(txt_b)
1201
1113
 
1202
1114
        # This is the result with LongestCommonSubstring matching
1203
 
        self.assertEquals(['--- a2\n',
1204
 
                           '+++ b2\n',
 
1115
        self.assertEquals(['--- a2 \n',
 
1116
                           '+++ b2 \n',
1205
1117
                           '@@ -1,6 +1,11 @@\n',
1206
1118
                           ' a\n',
1207
1119
                           ' b\n',
1217
1129
                          , list(unified_diff_files('a2', 'b2')))
1218
1130
 
1219
1131
        # And the patience diff
1220
 
        self.assertEquals(['--- a2\n',
1221
 
                           '+++ b2\n',
 
1132
        self.assertEquals(['--- a2 \n',
 
1133
                           '+++ b2 \n',
1222
1134
                           '@@ -4,6 +4,11 @@\n',
1223
1135
                           ' d\n',
1224
1136
                           ' e\n',
1238
1150
 
1239
1151
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1240
1152
 
1241
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1153
    _test_needs_features = [CompiledPatienceDiffFeature]
1242
1154
 
1243
1155
    def setUp(self):
1244
1156
        super(TestPatienceDiffLibFiles_c, self).setUp()
1245
 
        from bzrlib import _patiencediff_c
 
1157
        import bzrlib._patiencediff_c
1246
1158
        self._PatienceSequenceMatcher = \
1247
 
            _patiencediff_c.PatienceSequenceMatcher_c
1248
 
 
1249
 
 
1250
 
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1159
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1160
 
 
1161
 
 
1162
class TestUsingCompiledIfAvailable(TestCase):
1251
1163
 
1252
1164
    def test_PatienceSequenceMatcher(self):
1253
 
        if compiled_patiencediff_feature.available():
 
1165
        if CompiledPatienceDiffFeature.available():
1254
1166
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1255
1167
            self.assertIs(PatienceSequenceMatcher_c,
1256
 
                          patiencediff.PatienceSequenceMatcher)
 
1168
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1257
1169
        else:
1258
1170
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1259
1171
            self.assertIs(PatienceSequenceMatcher_py,
1260
 
                          patiencediff.PatienceSequenceMatcher)
 
1172
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1261
1173
 
1262
1174
    def test_unique_lcs(self):
1263
 
        if compiled_patiencediff_feature.available():
 
1175
        if CompiledPatienceDiffFeature.available():
1264
1176
            from bzrlib._patiencediff_c import unique_lcs_c
1265
1177
            self.assertIs(unique_lcs_c,
1266
 
                          patiencediff.unique_lcs)
 
1178
                          bzrlib.patiencediff.unique_lcs)
1267
1179
        else:
1268
1180
            from bzrlib._patiencediff_py import unique_lcs_py
1269
1181
            self.assertIs(unique_lcs_py,
1270
 
                          patiencediff.unique_lcs)
 
1182
                          bzrlib.patiencediff.unique_lcs)
1271
1183
 
1272
1184
    def test_recurse_matches(self):
1273
 
        if compiled_patiencediff_feature.available():
 
1185
        if CompiledPatienceDiffFeature.available():
1274
1186
            from bzrlib._patiencediff_c import recurse_matches_c
1275
1187
            self.assertIs(recurse_matches_c,
1276
 
                          patiencediff.recurse_matches)
 
1188
                          bzrlib.patiencediff.recurse_matches)
1277
1189
        else:
1278
1190
            from bzrlib._patiencediff_py import recurse_matches_py
1279
1191
            self.assertIs(recurse_matches_py,
1280
 
                          patiencediff.recurse_matches)
1281
 
 
1282
 
 
1283
 
class TestDiffFromTool(tests.TestCaseWithTransport):
1284
 
 
1285
 
    def test_from_string(self):
1286
 
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1287
 
        self.addCleanup(diff_obj.finish)
1288
 
        self.assertEqual(['diff', '@old_path', '@new_path'],
1289
 
            diff_obj.command_template)
1290
 
 
1291
 
    def test_from_string_u5(self):
1292
 
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1293
 
                                                 None, None, None)
1294
 
        self.addCleanup(diff_obj.finish)
1295
 
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1296
 
                         diff_obj.command_template)
1297
 
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1298
 
                         diff_obj._get_command('old-path', 'new-path'))
1299
 
 
1300
 
    def test_from_string_path_with_backslashes(self):
1301
 
        self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
1302
 
        tool = 'C:\\Tools\\Diff.exe'
1303
 
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1304
 
        self.addCleanup(diff_obj.finish)
1305
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
1306
 
                         diff_obj.command_template)
1307
 
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1308
 
                         diff_obj._get_command('old-path', 'new-path'))
1309
 
 
1310
 
    def test_execute(self):
1311
 
        output = StringIO()
1312
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1313
 
                                      'print "@old_path @new_path"'],
1314
 
                                     None, None, output)
1315
 
        self.addCleanup(diff_obj.finish)
1316
 
        diff_obj._execute('old', 'new')
1317
 
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1318
 
 
1319
 
    def test_excute_missing(self):
1320
 
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
 
                                     None, None, None)
1322
 
        self.addCleanup(diff_obj.finish)
1323
 
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1324
 
                              'old', 'new')
1325
 
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1326
 
                         ' on this machine', str(e))
1327
 
 
1328
 
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1329
 
        self.requireFeature(AttribFeature)
1330
 
        output = StringIO()
1331
 
        tree = self.make_branch_and_tree('tree')
1332
 
        self.build_tree_contents([('tree/file', 'content')])
1333
 
        tree.add('file', 'file-id')
1334
 
        tree.commit('old tree')
1335
 
        tree.lock_read()
1336
 
        self.addCleanup(tree.unlock)
1337
 
        basis_tree = tree.basis_tree()
1338
 
        basis_tree.lock_read()
1339
 
        self.addCleanup(basis_tree.unlock)
1340
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1341
 
                                      'print "@old_path @new_path"'],
1342
 
                                     basis_tree, tree, output)
1343
 
        diff_obj._prepare_files('file-id', 'file', 'file')
1344
 
        # The old content should be readonly
1345
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1346
 
                                    r'R.*old\\file$')
1347
 
        # The new content should use the tree object, not a 'new' file anymore
1348
 
        self.assertEndsWith(tree.basedir, 'work/tree')
1349
 
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1350
 
 
1351
 
    def assertReadableByAttrib(self, cwd, relpath, regex):
1352
 
        proc = subprocess.Popen(['attrib', relpath],
1353
 
                                stdout=subprocess.PIPE,
1354
 
                                cwd=cwd)
1355
 
        (result, err) = proc.communicate()
1356
 
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1357
 
 
1358
 
    def test_prepare_files(self):
1359
 
        output = StringIO()
1360
 
        tree = self.make_branch_and_tree('tree')
1361
 
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1362
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1363
 
        tree.add('oldname', 'file-id')
1364
 
        tree.add('oldname2', 'file2-id')
1365
 
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1366
 
        tree.commit('old tree', timestamp=315532800)
1367
 
        tree.rename_one('oldname', 'newname')
1368
 
        tree.rename_one('oldname2', 'newname2')
1369
 
        self.build_tree_contents([('tree/newname', 'newcontent')])
1370
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
1371
 
        old_tree = tree.basis_tree()
1372
 
        old_tree.lock_read()
1373
 
        self.addCleanup(old_tree.unlock)
1374
 
        tree.lock_read()
1375
 
        self.addCleanup(tree.unlock)
1376
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1377
 
                                      'print "@old_path @new_path"'],
1378
 
                                     old_tree, tree, output)
1379
 
        self.addCleanup(diff_obj.finish)
1380
 
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1381
 
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1382
 
                                                     'newname')
1383
 
        self.assertContainsRe(old_path, 'old/oldname$')
1384
 
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1385
 
        self.assertContainsRe(new_path, 'tree/newname$')
1386
 
        self.assertFileEqual('oldcontent', old_path)
1387
 
        self.assertFileEqual('newcontent', new_path)
1388
 
        if osutils.host_os_dereferences_symlinks():
1389
 
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
 
        # make sure we can create files with the same parent directories
1391
 
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1392
 
 
1393
 
 
1394
 
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1395
 
 
1396
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1397
 
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
1398
 
        TestGetTreesAndBranchesToDiff.
1399
 
        """
1400
 
        return diff.get_trees_and_branches_to_diff_locked(
1401
 
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1402
 
 
1403
 
    def test_basic(self):
1404
 
        tree = self.make_branch_and_tree('tree')
1405
 
        (old_tree, new_tree,
1406
 
         old_branch, new_branch,
1407
 
         specific_files, extra_trees) = self.call_gtabtd(
1408
 
             ['tree'], None, None, None)
1409
 
 
1410
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1411
 
        self.assertEqual(_mod_revision.NULL_REVISION,
1412
 
                         old_tree.get_revision_id())
1413
 
        self.assertEqual(tree.basedir, new_tree.basedir)
1414
 
        self.assertEqual(tree.branch.base, old_branch.base)
1415
 
        self.assertEqual(tree.branch.base, new_branch.base)
1416
 
        self.assertIs(None, specific_files)
1417
 
        self.assertIs(None, extra_trees)
1418
 
 
1419
 
    def test_with_rev_specs(self):
1420
 
        tree = self.make_branch_and_tree('tree')
1421
 
        self.build_tree_contents([('tree/file', 'oldcontent')])
1422
 
        tree.add('file', 'file-id')
1423
 
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1424
 
        self.build_tree_contents([('tree/file', 'newcontent')])
1425
 
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1426
 
 
1427
 
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1428
 
                     revisionspec.RevisionSpec.from_string('2')]
1429
 
        (old_tree, new_tree,
1430
 
         old_branch, new_branch,
1431
 
         specific_files, extra_trees) = self.call_gtabtd(
1432
 
            ['tree'], revisions, None, None)
1433
 
 
1434
 
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1435
 
        self.assertEqual("old-id", old_tree.get_revision_id())
1436
 
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1437
 
        self.assertEqual("new-id", new_tree.get_revision_id())
1438
 
        self.assertEqual(tree.branch.base, old_branch.base)
1439
 
        self.assertEqual(tree.branch.base, new_branch.base)
1440
 
        self.assertIs(None, specific_files)
1441
 
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
1442
 
 
1443
 
 
1444
 
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
1445
 
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
1446
 
    deprecated get_trees_and_branches_to_diff function.
1447
 
    """
1448
 
 
1449
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1450
 
        return self.applyDeprecated(
1451
 
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
1452
 
            path_list, revision_specs, old_url, new_url)
1453
 
 
 
1192
                          bzrlib.patiencediff.recurse_matches)