/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

More work on roundtrip push support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
import os
18
 
from cStringIO import StringIO
19
 
import errno
20
 
import subprocess
21
 
from tempfile import TemporaryFile
22
 
 
23
 
from bzrlib.diff import (
24
 
    internal_diff,
25
 
    external_diff,
26
 
    DiffPath,
27
 
    show_diff_trees,
28
 
    DiffSymlink,
29
 
    DiffTree,
30
 
    DiffText,
31
 
    )
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):
41
 
 
42
 
    def _probe(self):
43
 
        try:
44
 
            import bzrlib._patiencediff_c
45
 
        except ImportError:
46
 
            return False
47
 
        return True
48
 
 
49
 
    def feature_name(self):
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
 
        
83
 
 
84
 
def udiff_lines(old, new, allow_binary=False):
85
 
    output = StringIO()
86
 
    internal_diff('old', old, 'new', new, output, allow_binary)
87
 
    output.seek(0, 0)
88
 
    return output.readlines()
89
 
 
90
 
 
91
 
def external_udiff_lines(old, new, use_stringio=False):
92
 
    if use_stringio:
93
 
        # StringIO has no fileno, so it tests a different codepath
94
 
        output = StringIO()
95
 
    else:
96
 
        output = TemporaryFile()
97
 
    try:
98
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
99
 
    except NoDiff:
100
 
        raise TestSkipped('external "diff" not present to test')
101
 
    output.seek(0, 0)
102
 
    lines = output.readlines()
103
 
    output.close()
104
 
    return lines
105
 
 
106
 
 
107
 
class TestDiff(TestCase):
108
 
 
109
 
    def test_add_nl(self):
110
 
        """diff generates a valid diff for patches that add a newline"""
111
 
        lines = udiff_lines(['boo'], ['boo\n'])
112
 
        self.check_patch(lines)
113
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
114
 
            ## "expected no-nl, got %r" % lines[4]
115
 
 
116
 
    def test_add_nl_2(self):
117
 
        """diff generates a valid diff for patches that change last line and
118
 
        add a newline.
119
 
        """
120
 
        lines = udiff_lines(['boo'], ['goo\n'])
121
 
        self.check_patch(lines)
122
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
123
 
            ## "expected no-nl, got %r" % lines[4]
124
 
 
125
 
    def test_remove_nl(self):
126
 
        """diff generates a valid diff for patches that change last line and
127
 
        add a newline.
128
 
        """
129
 
        lines = udiff_lines(['boo\n'], ['boo'])
130
 
        self.check_patch(lines)
131
 
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
132
 
            ## "expected no-nl, got %r" % lines[5]
133
 
 
134
 
    def check_patch(self, lines):
135
 
        self.assert_(len(lines) > 1)
136
 
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
137
 
        self.assert_(lines[0].startswith ('---'))
138
 
            ## 'No orig line for patch:\n%s' % "".join(lines)
139
 
        self.assert_(lines[1].startswith ('+++'))
140
 
            ## 'No mod line for patch:\n%s' % "".join(lines)
141
 
        self.assert_(len(lines) > 2)
142
 
            ## "No hunks for patch:\n%s" % "".join(lines)
143
 
        self.assert_(lines[2].startswith('@@'))
144
 
            ## "No hunk header for patch:\n%s" % "".join(lines)
145
 
        self.assert_('@@' in lines[2][2:])
146
 
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
147
 
 
148
 
    def test_binary_lines(self):
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)
153
 
 
154
 
    def test_external_diff(self):
155
 
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
156
 
        self.check_patch(lines)
157
 
        self.assertEqual('\n', lines[-1])
158
 
 
159
 
    def test_external_diff_no_fileno(self):
160
 
        # Make sure that we can handle not having a fileno, even
161
 
        # if the diff is large
162
 
        lines = external_udiff_lines(['boo\n']*10000,
163
 
                                     ['goo\n']*10000,
164
 
                                     use_stringio=True)
165
 
        self.check_patch(lines)
166
 
 
167
 
    def test_external_diff_binary_lang_c(self):
168
 
        old_env = {}
169
 
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
170
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
171
 
        try:
172
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
173
 
            # Older versions of diffutils say "Binary files", newer
174
 
            # versions just say "Files".
175
 
            self.assertContainsRe(lines[0],
176
 
                                  '(Binary f|F)iles old and new differ\n')
177
 
            self.assertEquals(lines[1:], ['\n'])
178
 
        finally:
179
 
            for lang, old_val in old_env.iteritems():
180
 
                osutils.set_or_unset_env(lang, old_val)
181
 
 
182
 
    def test_no_external_diff(self):
183
 
        """Check that NoDiff is raised when diff is not available"""
184
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
185
 
        orig_path = os.environ['PATH']
186
 
        try:
187
 
            os.environ['PATH'] = ''
188
 
            self.assertRaises(NoDiff, external_diff,
189
 
                              'old', ['boo\n'], 'new', ['goo\n'],
190
 
                              StringIO(), diff_opts=['-u'])
191
 
        finally:
192
 
            os.environ['PATH'] = orig_path
193
 
        
194
 
    def test_internal_diff_default(self):
195
 
        # Default internal diff encoding is utf8
196
 
        output = StringIO()
197
 
        internal_diff(u'old_\xb5', ['old_text\n'],
198
 
                    u'new_\xe5', ['new_text\n'], output)
199
 
        lines = output.getvalue().splitlines(True)
200
 
        self.check_patch(lines)
201
 
        self.assertEquals(['--- old_\xc2\xb5\n',
202
 
                           '+++ new_\xc3\xa5\n',
203
 
                           '@@ -1,1 +1,1 @@\n',
204
 
                           '-old_text\n',
205
 
                           '+new_text\n',
206
 
                           '\n',
207
 
                          ]
208
 
                          , lines)
209
 
 
210
 
    def test_internal_diff_utf8(self):
211
 
        output = StringIO()
212
 
        internal_diff(u'old_\xb5', ['old_text\n'],
213
 
                    u'new_\xe5', ['new_text\n'], output,
214
 
                    path_encoding='utf8')
215
 
        lines = output.getvalue().splitlines(True)
216
 
        self.check_patch(lines)
217
 
        self.assertEquals(['--- old_\xc2\xb5\n',
218
 
                           '+++ new_\xc3\xa5\n',
219
 
                           '@@ -1,1 +1,1 @@\n',
220
 
                           '-old_text\n',
221
 
                           '+new_text\n',
222
 
                           '\n',
223
 
                          ]
224
 
                          , lines)
225
 
 
226
 
    def test_internal_diff_iso_8859_1(self):
227
 
        output = StringIO()
228
 
        internal_diff(u'old_\xb5', ['old_text\n'],
229
 
                    u'new_\xe5', ['new_text\n'], output,
230
 
                    path_encoding='iso-8859-1')
231
 
        lines = output.getvalue().splitlines(True)
232
 
        self.check_patch(lines)
233
 
        self.assertEquals(['--- old_\xb5\n',
234
 
                           '+++ new_\xe5\n',
235
 
                           '@@ -1,1 +1,1 @@\n',
236
 
                           '-old_text\n',
237
 
                           '+new_text\n',
238
 
                           '\n',
239
 
                          ]
240
 
                          , lines)
241
 
 
242
 
    def test_internal_diff_no_content(self):
243
 
        output = StringIO()
244
 
        internal_diff(u'old', [], u'new', [], output)
245
 
        self.assertEqual('', output.getvalue())
246
 
 
247
 
    def test_internal_diff_no_changes(self):
248
 
        output = StringIO()
249
 
        internal_diff(u'old', ['text\n', 'contents\n'],
250
 
                      u'new', ['text\n', 'contents\n'],
251
 
                      output)
252
 
        self.assertEqual('', output.getvalue())
253
 
 
254
 
    def test_internal_diff_returns_bytes(self):
255
 
        import StringIO
256
 
        output = StringIO.StringIO()
257
 
        internal_diff(u'old_\xb5', ['old_text\n'],
258
 
                    u'new_\xe5', ['new_text\n'], output)
259
 
        self.failUnless(isinstance(output.getvalue(), str),
260
 
            'internal_diff should return bytestrings')
261
 
 
262
 
 
263
 
class TestDiffFiles(TestCaseInTempDir):
264
 
 
265
 
    def test_external_diff_binary(self):
266
 
        """The output when using external diff should use diff's i18n error"""
267
 
        # Make sure external_diff doesn't fail in the current LANG
268
 
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
269
 
 
270
 
        cmd = ['diff', '-u', '--binary', 'old', 'new']
271
 
        open('old', 'wb').write('\x00foobar\n')
272
 
        open('new', 'wb').write('foo\x00bar\n')
273
 
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
274
 
                                     stdin=subprocess.PIPE)
275
 
        out, err = pipe.communicate()
276
 
        # Diff returns '2' on Binary files.
277
 
        self.assertEqual(2, pipe.returncode)
278
 
        # We should output whatever diff tells us, plus a trailing newline
279
 
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
280
 
 
281
 
 
282
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
283
 
    """Has a helper for running show_diff_trees"""
284
 
 
285
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
286
 
        output = StringIO()
287
 
        if working_tree is not None:
288
 
            extra_trees = (working_tree,)
289
 
        else:
290
 
            extra_trees = ()
291
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
292
 
                        extra_trees=extra_trees, old_label='old/',
293
 
                        new_label='new/')
294
 
        return output.getvalue()
295
 
 
296
 
 
297
 
class TestDiffDates(TestShowDiffTreesHelper):
298
 
 
299
 
    def setUp(self):
300
 
        super(TestDiffDates, self).setUp()
301
 
        self.wt = self.make_branch_and_tree('.')
302
 
        self.b = self.wt.branch
303
 
        self.build_tree_contents([
304
 
            ('file1', 'file1 contents at rev 1\n'),
305
 
            ('file2', 'file2 contents at rev 1\n')
306
 
            ])
307
 
        self.wt.add(['file1', 'file2'])
308
 
        self.wt.commit(
309
 
            message='Revision 1',
310
 
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
311
 
            timezone=0,
312
 
            rev_id='rev-1')
313
 
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
314
 
        self.wt.commit(
315
 
            message='Revision 2',
316
 
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
317
 
            timezone=28800,
318
 
            rev_id='rev-2')
319
 
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
320
 
        self.wt.commit(
321
 
            message='Revision 3',
322
 
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
323
 
            timezone=-3600,
324
 
            rev_id='rev-3')
325
 
        self.wt.remove(['file2'])
326
 
        self.wt.commit(
327
 
            message='Revision 4',
328
 
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
329
 
            timezone=0,
330
 
            rev_id='rev-4')
331
 
        self.build_tree_contents([
332
 
            ('file1', 'file1 contents in working tree\n')
333
 
            ])
334
 
        # set the date stamps for files in the working tree to known values
335
 
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
336
 
 
337
 
    def test_diff_rev_tree_working_tree(self):
338
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
339
 
        # note that the date for old/file1 is from rev 2 rather than from
340
 
        # the basis revision (rev 4)
341
 
        self.assertEqualDiff(output, '''\
342
 
=== modified file 'file1'
343
 
--- old/file1\t2006-04-02 00:00:00 +0000
344
 
+++ new/file1\t2006-04-05 00:00:00 +0000
345
 
@@ -1,1 +1,1 @@
346
 
-file1 contents at rev 2
347
 
+file1 contents in working tree
348
 
 
349
 
''')
350
 
 
351
 
    def test_diff_rev_tree_rev_tree(self):
352
 
        tree1 = self.b.repository.revision_tree('rev-2')
353
 
        tree2 = self.b.repository.revision_tree('rev-3')
354
 
        output = self.get_diff(tree1, tree2)
355
 
        self.assertEqualDiff(output, '''\
356
 
=== modified file 'file2'
357
 
--- old/file2\t2006-04-01 00:00:00 +0000
358
 
+++ new/file2\t2006-04-03 00:00:00 +0000
359
 
@@ -1,1 +1,1 @@
360
 
-file2 contents at rev 1
361
 
+file2 contents at rev 3
362
 
 
363
 
''')
364
 
        
365
 
    def test_diff_add_files(self):
366
 
        tree1 = self.b.repository.revision_tree(None)
367
 
        tree2 = self.b.repository.revision_tree('rev-1')
368
 
        output = self.get_diff(tree1, tree2)
369
 
        # the files have the epoch time stamp for the tree in which
370
 
        # they don't exist.
371
 
        self.assertEqualDiff(output, '''\
372
 
=== added file 'file1'
373
 
--- old/file1\t1970-01-01 00:00:00 +0000
374
 
+++ new/file1\t2006-04-01 00:00:00 +0000
375
 
@@ -0,0 +1,1 @@
376
 
+file1 contents at rev 1
377
 
 
378
 
=== added file 'file2'
379
 
--- old/file2\t1970-01-01 00:00:00 +0000
380
 
+++ new/file2\t2006-04-01 00:00:00 +0000
381
 
@@ -0,0 +1,1 @@
382
 
+file2 contents at rev 1
383
 
 
384
 
''')
385
 
 
386
 
    def test_diff_remove_files(self):
387
 
        tree1 = self.b.repository.revision_tree('rev-3')
388
 
        tree2 = self.b.repository.revision_tree('rev-4')
389
 
        output = self.get_diff(tree1, tree2)
390
 
        # the file has the epoch time stamp for the tree in which
391
 
        # it doesn't exist.
392
 
        self.assertEqualDiff(output, '''\
393
 
=== removed file 'file2'
394
 
--- old/file2\t2006-04-03 00:00:00 +0000
395
 
+++ new/file2\t1970-01-01 00:00:00 +0000
396
 
@@ -1,1 +0,0 @@
397
 
-file2 contents at rev 3
398
 
 
399
 
''')
400
 
 
401
 
    def test_show_diff_specified(self):
402
 
        """A working tree filename can be used to identify a file"""
403
 
        self.wt.rename_one('file1', 'file1b')
404
 
        old_tree = self.b.repository.revision_tree('rev-1')
405
 
        new_tree = self.b.repository.revision_tree('rev-4')
406
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
407
 
                            working_tree=self.wt)
408
 
        self.assertContainsRe(out, 'file1\t')
409
 
 
410
 
    def test_recursive_diff(self):
411
 
        """Children of directories are matched"""
412
 
        os.mkdir('dir1')
413
 
        os.mkdir('dir2')
414
 
        self.wt.add(['dir1', 'dir2'])
415
 
        self.wt.rename_one('file1', 'dir1/file1')
416
 
        old_tree = self.b.repository.revision_tree('rev-1')
417
 
        new_tree = self.b.repository.revision_tree('rev-4')
418
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
419
 
                            working_tree=self.wt)
420
 
        self.assertContainsRe(out, 'file1\t')
421
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
422
 
                            working_tree=self.wt)
423
 
        self.assertNotContainsRe(out, 'file1\t')
424
 
 
425
 
 
426
 
 
427
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
428
 
    """Direct tests for show_diff_trees"""
429
 
 
430
 
    def test_modified_file(self):
431
 
        """Test when a file is modified."""
432
 
        tree = self.make_branch_and_tree('tree')
433
 
        self.build_tree_contents([('tree/file', 'contents\n')])
434
 
        tree.add(['file'], ['file-id'])
435
 
        tree.commit('one', rev_id='rev-1')
436
 
 
437
 
        self.build_tree_contents([('tree/file', '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')
444
 
 
445
 
    def test_modified_file_in_renamed_dir(self):
446
 
        """Test when a file is modified in a renamed directory."""
447
 
        tree = self.make_branch_and_tree('tree')
448
 
        self.build_tree(['tree/dir/'])
449
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
450
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
451
 
        tree.commit('one', rev_id='rev-1')
452
 
 
453
 
        tree.rename_one('dir', 'other')
454
 
        self.build_tree_contents([('tree/other/file', 'new contents\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")
458
 
        # XXX: This is technically incorrect, because it used to be at another
459
 
        # location. What to do?
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')
464
 
 
465
 
    def test_renamed_directory(self):
466
 
        """Test when only a directory is only renamed."""
467
 
        tree = self.make_branch_and_tree('tree')
468
 
        self.build_tree(['tree/dir/'])
469
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
470
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
471
 
        tree.commit('one', rev_id='rev-1')
472
 
 
473
 
        tree.rename_one('dir', 'newdir')
474
 
        diff = self.get_diff(tree.basis_tree(), tree)
475
 
        # Renaming a directory should be a single "you renamed this dir" even
476
 
        # when there are files inside.
477
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
478
 
 
479
 
    def test_renamed_file(self):
480
 
        """Test when a file is only renamed."""
481
 
        tree = self.make_branch_and_tree('tree')
482
 
        self.build_tree_contents([('tree/file', 'contents\n')])
483
 
        tree.add(['file'], ['file-id'])
484
 
        tree.commit('one', rev_id='rev-1')
485
 
 
486
 
        tree.rename_one('file', 'newname')
487
 
        diff = self.get_diff(tree.basis_tree(), tree)
488
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
489
 
        # We shouldn't have a --- or +++ line, because there is no content
490
 
        # change
491
 
        self.assertNotContainsRe(diff, '---')
492
 
 
493
 
    def test_renamed_and_modified_file(self):
494
 
        """Test when a file is only renamed."""
495
 
        tree = self.make_branch_and_tree('tree')
496
 
        self.build_tree_contents([('tree/file', 'contents\n')])
497
 
        tree.add(['file'], ['file-id'])
498
 
        tree.commit('one', rev_id='rev-1')
499
 
 
500
 
        tree.rename_one('file', 'newname')
501
 
        self.build_tree_contents([('tree/newname', 'new contents\n')])
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')
508
 
 
509
 
    def test_binary_unicode_filenames(self):
510
 
        """Test that contents of files are *not* encoded in UTF-8 when there
511
 
        is a binary file in the diff.
512
 
        """
513
 
        # See https://bugs.launchpad.net/bugs/110092.
514
 
        self.requireFeature(UnicodeFilename)
515
 
 
516
 
        # This bug isn't triggered with cStringIO.
517
 
        from StringIO import StringIO
518
 
        tree = self.make_branch_and_tree('tree')
519
 
        alpha, omega = u'\u03b1', u'\u03c9'
520
 
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
521
 
        self.build_tree_contents(
522
 
            [('tree/' + alpha, chr(0)),
523
 
             ('tree/' + omega,
524
 
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
525
 
        tree.add([alpha], ['file-id'])
526
 
        tree.add([omega], ['file-id-2'])
527
 
        diff_content = StringIO()
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,))
536
 
 
537
 
    def test_unicode_filename(self):
538
 
        """Test when the filename are unicode."""
539
 
        self.requireFeature(UnicodeFilename)
540
 
 
541
 
        alpha, omega = u'\u03b1', u'\u03c9'
542
 
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
543
 
 
544
 
        tree = self.make_branch_and_tree('tree')
545
 
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
546
 
        tree.add(['ren_'+alpha], ['file-id-2'])
547
 
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
548
 
        tree.add(['del_'+alpha], ['file-id-3'])
549
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
550
 
        tree.add(['mod_'+alpha], ['file-id-4'])
551
 
 
552
 
        tree.commit('one', rev_id='rev-1')
553
 
 
554
 
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
555
 
        tree.remove('del_'+alpha)
556
 
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
557
 
        tree.add(['add_'+alpha], ['file-id'])
558
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
559
 
 
560
 
        diff = self.get_diff(tree.basis_tree(), tree)
561
 
        self.assertContainsRe(diff,
562
 
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
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):
569
 
 
570
 
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
571
 
        self.to_file.write('was: ')
572
 
        self.to_file.write(self.old_tree.get_file(file_id).read())
573
 
        self.to_file.write('is: ')
574
 
        self.to_file.write(self.new_tree.get_file(file_id).read())
575
 
        pass
576
 
 
577
 
 
578
 
class TestDiffTree(TestCaseWithTransport):
579
 
 
580
 
    def setUp(self):
581
 
        TestCaseWithTransport.setUp(self)
582
 
        self.old_tree = self.make_branch_and_tree('old-tree')
583
 
        self.old_tree.lock_write()
584
 
        self.addCleanup(self.old_tree.unlock)
585
 
        self.new_tree = self.make_branch_and_tree('new-tree')
586
 
        self.new_tree.lock_write()
587
 
        self.addCleanup(self.new_tree.unlock)
588
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
589
 
 
590
 
    def test_diff_text(self):
591
 
        self.build_tree_contents([('old-tree/olddir/',),
592
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
593
 
        self.old_tree.add('olddir')
594
 
        self.old_tree.add('olddir/oldfile', 'file-id')
595
 
        self.build_tree_contents([('new-tree/newdir/',),
596
 
                                  ('new-tree/newdir/newfile', 'new\n')])
597
 
        self.new_tree.add('newdir')
598
 
        self.new_tree.add('newdir/newfile', 'file-id')
599
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
600
 
        differ.diff_text('file-id', None, 'old label', 'new label')
601
 
        self.assertEqual(
602
 
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
603
 
            differ.to_file.getvalue())
604
 
        differ.to_file.seek(0)
605
 
        differ.diff_text(None, 'file-id', 'old label', 'new label')
606
 
        self.assertEqual(
607
 
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
608
 
            differ.to_file.getvalue())
609
 
        differ.to_file.seek(0)
610
 
        differ.diff_text('file-id', 'file-id', 'old label', 'new label')
611
 
        self.assertEqual(
612
 
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
613
 
            differ.to_file.getvalue())
614
 
 
615
 
    def test_diff_deletion(self):
616
 
        self.build_tree_contents([('old-tree/file', 'contents'),
617
 
                                  ('new-tree/file', 'contents')])
618
 
        self.old_tree.add('file', 'file-id')
619
 
        self.new_tree.add('file', 'file-id')
620
 
        os.unlink('new-tree/file')
621
 
        self.differ.show_diff(None)
622
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
623
 
 
624
 
    def test_diff_creation(self):
625
 
        self.build_tree_contents([('old-tree/file', 'contents'),
626
 
                                  ('new-tree/file', 'contents')])
627
 
        self.old_tree.add('file', 'file-id')
628
 
        self.new_tree.add('file', 'file-id')
629
 
        os.unlink('old-tree/file')
630
 
        self.differ.show_diff(None)
631
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
632
 
 
633
 
    def test_diff_symlink(self):
634
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
635
 
        differ.diff_symlink('old target', None)
636
 
        self.assertEqual("=== target was 'old target'\n",
637
 
                         differ.to_file.getvalue())
638
 
 
639
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
640
 
        differ.diff_symlink(None, 'new target')
641
 
        self.assertEqual("=== target is 'new target'\n",
642
 
                         differ.to_file.getvalue())
643
 
 
644
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
645
 
        differ.diff_symlink('old target', 'new target')
646
 
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
647
 
                         differ.to_file.getvalue())
648
 
 
649
 
    def test_diff(self):
650
 
        self.build_tree_contents([('old-tree/olddir/',),
651
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
652
 
        self.old_tree.add('olddir')
653
 
        self.old_tree.add('olddir/oldfile', 'file-id')
654
 
        self.build_tree_contents([('new-tree/newdir/',),
655
 
                                  ('new-tree/newdir/newfile', 'new\n')])
656
 
        self.new_tree.add('newdir')
657
 
        self.new_tree.add('newdir/newfile', 'file-id')
658
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
659
 
        self.assertContainsRe(
660
 
            self.differ.to_file.getvalue(),
661
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
662
 
             ' \@\@\n-old\n\+new\n\n')
663
 
 
664
 
    def test_diff_kind_change(self):
665
 
        self.build_tree_contents([('old-tree/olddir/',),
666
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
667
 
        self.old_tree.add('olddir')
668
 
        self.old_tree.add('olddir/oldfile', 'file-id')
669
 
        self.build_tree(['new-tree/newdir/'])
670
 
        os.symlink('new', 'new-tree/newdir/newfile')
671
 
        self.new_tree.add('newdir')
672
 
        self.new_tree.add('newdir/newfile', 'file-id')
673
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
674
 
        self.assertContainsRe(
675
 
            self.differ.to_file.getvalue(),
676
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
677
 
             ' \@\@\n-old\n\n')
678
 
        self.assertContainsRe(self.differ.to_file.getvalue(),
679
 
                              "=== target is 'new'\n")
680
 
 
681
 
    def test_diff_directory(self):
682
 
        self.build_tree(['new-tree/new-dir/'])
683
 
        self.new_tree.add('new-dir', 'new-dir-id')
684
 
        self.differ.diff('new-dir-id', None, 'new-dir')
685
 
        self.assertEqual(self.differ.to_file.getvalue(), '')
686
 
 
687
 
    def create_old_new(self):
688
 
        self.build_tree_contents([('old-tree/olddir/',),
689
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
690
 
        self.old_tree.add('olddir')
691
 
        self.old_tree.add('olddir/oldfile', 'file-id')
692
 
        self.build_tree_contents([('new-tree/newdir/',),
693
 
                                  ('new-tree/newdir/newfile', 'new\n')])
694
 
        self.new_tree.add('newdir')
695
 
        self.new_tree.add('newdir/newfile', 'file-id')
696
 
 
697
 
    def test_register_diff(self):
698
 
        self.create_old_new()
699
 
        old_diff_factories = DiffTree.diff_factories
700
 
        DiffTree.diff_factories=old_diff_factories[:]
701
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
702
 
        try:
703
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
704
 
        finally:
705
 
            DiffTree.diff_factories = old_diff_factories
706
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
707
 
        self.assertNotContainsRe(
708
 
            differ.to_file.getvalue(),
709
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
710
 
             ' \@\@\n-old\n\+new\n\n')
711
 
        self.assertContainsRe(differ.to_file.getvalue(),
712
 
                              'was: old\nis: new\n')
713
 
 
714
 
    def test_extra_factories(self):
715
 
        self.create_old_new()
716
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
717
 
                            extra_factories=[DiffWasIs.from_diff_tree])
718
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
719
 
        self.assertNotContainsRe(
720
 
            differ.to_file.getvalue(),
721
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
722
 
             ' \@\@\n-old\n\+new\n\n')
723
 
        self.assertContainsRe(differ.to_file.getvalue(),
724
 
                              'was: old\nis: new\n')
725
 
 
726
 
 
727
 
class TestPatienceDiffLib(TestCase):
728
 
 
729
 
    def setUp(self):
730
 
        super(TestPatienceDiffLib, self).setUp()
731
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
732
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
733
 
        self._PatienceSequenceMatcher = \
734
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
735
 
 
736
 
    def test_unique_lcs(self):
737
 
        unique_lcs = self._unique_lcs
738
 
        self.assertEquals(unique_lcs('', ''), [])
739
 
        self.assertEquals(unique_lcs('', 'a'), [])
740
 
        self.assertEquals(unique_lcs('a', ''), [])
741
 
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
742
 
        self.assertEquals(unique_lcs('a', 'b'), [])
743
 
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
744
 
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
745
 
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
746
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
747
 
                                                         (3,3), (4,4)])
748
 
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
749
 
 
750
 
    def test_recurse_matches(self):
751
 
        def test_one(a, b, matches):
752
 
            test_matches = []
753
 
            self._recurse_matches(
754
 
                a, b, 0, 0, len(a), len(b), test_matches, 10)
755
 
            self.assertEquals(test_matches, matches)
756
 
 
757
 
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
758
 
                 [(0, 0), (2, 2), (4, 4)])
759
 
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
760
 
                 [(0, 0), (2, 1), (4, 2)])
761
 
        # Even though 'bc' is not unique globally, and is surrounded by
762
 
        # non-matching lines, we should still match, because they are locally
763
 
        # unique
764
 
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
765
 
                                          (4, 6), (5, 7), (6, 8)])
766
 
 
767
 
        # recurse_matches doesn't match non-unique 
768
 
        # lines surrounded by bogus text.
769
 
        # The update has been done in patiencediff.SequenceMatcher instead
770
 
 
771
 
        # This is what it could be
772
 
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
773
 
 
774
 
        # This is what it currently gives:
775
 
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
776
 
 
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
 
 
786
 
        # Some basic matching tests
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 
799
 
        # a copied block stays attached to the previous section,
800
 
        # not the later one.
801
 
        # difflib would tend to grab the trailing longest match
802
 
        # which would make the diff not look right
803
 
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
804
 
                   [(0, 0, 6), (6, 11, 10)])
805
 
 
806
 
        # make sure it supports passing in lists
807
 
        chk_blocks(
808
 
                   ['hello there\n',
809
 
                    'world\n',
810
 
                    'how are you today?\n'],
811
 
                   ['hello there\n',
812
 
                    'how are you today?\n'],
813
 
                [(0, 0, 1), (2, 1, 1)])
814
 
 
815
 
        # non unique lines surrounded by non-matching lines
816
 
        # won't be found
817
 
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
818
 
 
819
 
        # But they only need to be locally unique
820
 
        chk_blocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
821
 
 
822
 
        # non unique blocks won't be matched
823
 
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
824
 
 
825
 
        # but locally unique ones will
826
 
        chk_blocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
827
 
                                              (5,4,1), (7,5,2), (10,8,1)])
828
 
 
829
 
        chk_blocks('abbabbXd', 'cabbabxd', [(7,7,1)])
830
 
        chk_blocks('abbabbbb', 'cabbabbc', [])
831
 
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [])
832
 
 
833
 
    def test_opcodes(self):
834
 
        def chk_ops(a, b, expected_codes):
835
 
            s = self._PatienceSequenceMatcher(None, a, b)
836
 
            self.assertEquals(expected_codes, s.get_opcodes())
837
 
 
838
 
        chk_ops('', '', [])
839
 
        chk_ops([], [], [])
840
 
        chk_ops('abc', '', [('delete', 0,3, 0,0)])
841
 
        chk_ops('', 'abc', [('insert', 0,0, 0,3)])
842
 
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
843
 
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
844
 
                                 ('replace', 3,4, 3,4)
845
 
                                ])
846
 
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
847
 
                                 ('equal',  1,4, 0,3),
848
 
                                 ('insert', 4,4, 3,4)
849
 
                                ])
850
 
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
851
 
                                  ('equal',  1,5, 0,4)
852
 
                                 ])
853
 
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
854
 
                                   ('replace', 2,3, 2,3),
855
 
                                   ('equal',   3,5, 3,5)
856
 
                                  ])
857
 
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
858
 
                                     ('replace', 2,3, 2,5),
859
 
                                     ('equal',   3,5, 5,7)
860
 
                                    ])
861
 
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
862
 
                                    ('insert', 2,2, 2,5),
863
 
                                    ('equal',  2,4, 5,7)
864
 
                                   ])
865
 
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
866
 
                [('equal',  0,6,  0,6),
867
 
                 ('insert', 6,6,  6,11),
868
 
                 ('equal',  6,16, 11,21)
869
 
                ])
870
 
        chk_ops(
871
 
                [ 'hello there\n'
872
 
                , 'world\n'
873
 
                , 'how are you today?\n'],
874
 
                [ 'hello there\n'
875
 
                , 'how are you today?\n'],
876
 
                [('equal',  0,1, 0,1),
877
 
                 ('delete', 1,2, 1,1),
878
 
                 ('equal',  2,3, 1,2),
879
 
                ])
880
 
        chk_ops('aBccDe', 'abccde', 
881
 
                [('equal',   0,1, 0,1),
882
 
                 ('replace', 1,5, 1,5),
883
 
                 ('equal',   5,6, 5,6),
884
 
                ])
885
 
        chk_ops('aBcDec', 'abcdec', 
886
 
                [('equal',   0,1, 0,1),
887
 
                 ('replace', 1,2, 1,2),
888
 
                 ('equal',   2,3, 2,3),
889
 
                 ('replace', 3,4, 3,4),
890
 
                 ('equal',   4,6, 4,6),
891
 
                ])
892
 
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
893
 
                [('equal',   0,1, 0,1),
894
 
                 ('replace', 1,8, 1,8),
895
 
                 ('equal',   8,9, 8,9)
896
 
                ])
897
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
898
 
                [('equal',   0,1, 0,1),
899
 
                 ('replace', 1,2, 1,2),
900
 
                 ('equal',   2,4, 2,4),
901
 
                 ('delete', 4,5, 4,4),
902
 
                 ('equal',   5,6, 4,5),
903
 
                 ('delete', 6,7, 5,5),
904
 
                 ('equal',   7,9, 5,7),
905
 
                 ('replace', 9,10, 7,8),
906
 
                 ('equal',   10,11, 8,9)
907
 
                ])
908
 
 
909
 
    def test_grouped_opcodes(self):
910
 
        def chk_ops(a, b, expected_codes, n=3):
911
 
            s = self._PatienceSequenceMatcher(None, a, b)
912
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
913
 
 
914
 
        chk_ops('', '', [])
915
 
        chk_ops([], [], [])
916
 
        chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
917
 
        chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
918
 
        chk_ops('abcd', 'abcd', [])
919
 
        chk_ops('abcd', 'abce', [[('equal',   0,3, 0,3),
920
 
                                  ('replace', 3,4, 3,4)
921
 
                                 ]])
922
 
        chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
923
 
                                 ('equal',  1,4, 0,3),
924
 
                                 ('insert', 4,4, 3,4)
925
 
                                ]])
926
 
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
927
 
                [[('equal',  3,6, 3,6),
928
 
                  ('insert', 6,6, 6,11),
929
 
                  ('equal',  6,9, 11,14)
930
 
                  ]])
931
 
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
932
 
                [[('equal',  2,6, 2,6),
933
 
                  ('insert', 6,6, 6,11),
934
 
                  ('equal',  6,10, 11,15)
935
 
                  ]], 4)
936
 
        chk_ops('Xabcdef', 'abcdef',
937
 
                [[('delete', 0,1, 0,0),
938
 
                  ('equal',  1,4, 0,3)
939
 
                  ]])
940
 
        chk_ops('abcdef', 'abcdefX',
941
 
                [[('equal',  3,6, 3,6),
942
 
                  ('insert', 6,6, 6,7)
943
 
                  ]])
944
 
 
945
 
 
946
 
    def test_multiple_ranges(self):
947
 
        # There was an earlier bug where we used a bad set of ranges,
948
 
        # this triggers that specific bug, to make sure it doesn't regress
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)])
965
 
 
966
 
        # These are rot13 code snippets.
967
 
        chk_blocks('''\
968
 
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
969
 
    """
970
 
    gnxrf_netf = ['svyr*']
971
 
    gnxrf_bcgvbaf = ['ab-erphefr']
972
 
  
973
 
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
974
 
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
975
 
        vs vf_dhvrg():
976
 
            ercbegre = nqq_ercbegre_ahyy
977
 
        ryfr:
978
 
            ercbegre = nqq_ercbegre_cevag
979
 
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
980
 
 
981
 
 
982
 
pynff pzq_zxqve(Pbzznaq):
983
 
'''.splitlines(True), '''\
984
 
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
985
 
 
986
 
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
987
 
    nqq gurz.
988
 
    """
989
 
    gnxrf_netf = ['svyr*']
990
 
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
991
 
 
992
 
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
993
 
        vzcbeg omeyvo.nqq
994
 
 
995
 
        vs qel_eha:
996
 
            vs vf_dhvrg():
997
 
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
998
 
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
999
 
            ryfr:
1000
 
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
1001
 
        ryvs vf_dhvrg():
1002
 
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
1003
 
        ryfr:
1004
 
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1005
 
 
1006
 
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1007
 
 
1008
 
 
1009
 
pynff pzq_zxqve(Pbzznaq):
1010
 
'''.splitlines(True)
1011
 
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1012
 
 
1013
 
    def test_patience_unified_diff(self):
1014
 
        txt_a = ['hello there\n',
1015
 
                 'world\n',
1016
 
                 'how are you today?\n']
1017
 
        txt_b = ['hello there\n',
1018
 
                 'how are you today?\n']
1019
 
        unified_diff = bzrlib.patiencediff.unified_diff
1020
 
        psm = self._PatienceSequenceMatcher
1021
 
        self.assertEquals([ '---  \n',
1022
 
                           '+++  \n',
1023
 
                           '@@ -1,3 +1,2 @@\n',
1024
 
                           ' hello there\n',
1025
 
                           '-world\n',
1026
 
                           ' how are you today?\n'
1027
 
                          ]
1028
 
                          , list(unified_diff(txt_a, txt_b,
1029
 
                                 sequencematcher=psm)))
1030
 
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1031
 
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1032
 
        # This is the result with LongestCommonSubstring matching
1033
 
        self.assertEquals(['---  \n',
1034
 
                           '+++  \n',
1035
 
                           '@@ -1,6 +1,11 @@\n',
1036
 
                           ' a\n',
1037
 
                           ' b\n',
1038
 
                           ' c\n',
1039
 
                           '+d\n',
1040
 
                           '+e\n',
1041
 
                           '+f\n',
1042
 
                           '+x\n',
1043
 
                           '+y\n',
1044
 
                           ' d\n',
1045
 
                           ' e\n',
1046
 
                           ' f\n']
1047
 
                          , list(unified_diff(txt_a, txt_b)))
1048
 
        # And the patience diff
1049
 
        self.assertEquals(['---  \n',
1050
 
                           '+++  \n',
1051
 
                           '@@ -4,6 +4,11 @@\n',
1052
 
                           ' d\n',
1053
 
                           ' e\n',
1054
 
                           ' f\n',
1055
 
                           '+x\n',
1056
 
                           '+y\n',
1057
 
                           '+d\n',
1058
 
                           '+e\n',
1059
 
                           '+f\n',
1060
 
                           ' g\n',
1061
 
                           ' h\n',
1062
 
                           ' i\n',
1063
 
                          ]
1064
 
                          , list(unified_diff(txt_a, txt_b,
1065
 
                                 sequencematcher=psm)))
1066
 
 
1067
 
 
1068
 
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1069
 
 
1070
 
    _test_needs_features = [CompiledPatienceDiffFeature]
1071
 
 
1072
 
    def setUp(self):
1073
 
        super(TestPatienceDiffLib_c, self).setUp()
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
1077
 
        self._PatienceSequenceMatcher = \
1078
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1079
 
 
1080
 
 
1081
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1082
 
 
1083
 
    def setUp(self):
1084
 
        super(TestPatienceDiffLibFiles, self).setUp()
1085
 
        self._PatienceSequenceMatcher = \
1086
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1087
 
 
1088
 
    def test_patience_unified_diff_files(self):
1089
 
        txt_a = ['hello there\n',
1090
 
                 'world\n',
1091
 
                 'how are you today?\n']
1092
 
        txt_b = ['hello there\n',
1093
 
                 'how are you today?\n']
1094
 
        open('a1', 'wb').writelines(txt_a)
1095
 
        open('b1', 'wb').writelines(txt_b)
1096
 
 
1097
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
1098
 
        psm = self._PatienceSequenceMatcher
1099
 
        self.assertEquals(['--- a1 \n',
1100
 
                           '+++ b1 \n',
1101
 
                           '@@ -1,3 +1,2 @@\n',
1102
 
                           ' hello there\n',
1103
 
                           '-world\n',
1104
 
                           ' how are you today?\n',
1105
 
                          ]
1106
 
                          , list(unified_diff_files('a1', 'b1',
1107
 
                                 sequencematcher=psm)))
1108
 
 
1109
 
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1110
 
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1111
 
        open('a2', 'wb').writelines(txt_a)
1112
 
        open('b2', 'wb').writelines(txt_b)
1113
 
 
1114
 
        # This is the result with LongestCommonSubstring matching
1115
 
        self.assertEquals(['--- a2 \n',
1116
 
                           '+++ b2 \n',
1117
 
                           '@@ -1,6 +1,11 @@\n',
1118
 
                           ' a\n',
1119
 
                           ' b\n',
1120
 
                           ' c\n',
1121
 
                           '+d\n',
1122
 
                           '+e\n',
1123
 
                           '+f\n',
1124
 
                           '+x\n',
1125
 
                           '+y\n',
1126
 
                           ' d\n',
1127
 
                           ' e\n',
1128
 
                           ' f\n']
1129
 
                          , list(unified_diff_files('a2', 'b2')))
1130
 
 
1131
 
        # And the patience diff
1132
 
        self.assertEquals(['--- a2 \n',
1133
 
                           '+++ b2 \n',
1134
 
                           '@@ -4,6 +4,11 @@\n',
1135
 
                           ' d\n',
1136
 
                           ' e\n',
1137
 
                           ' f\n',
1138
 
                           '+x\n',
1139
 
                           '+y\n',
1140
 
                           '+d\n',
1141
 
                           '+e\n',
1142
 
                           '+f\n',
1143
 
                           ' g\n',
1144
 
                           ' h\n',
1145
 
                           ' i\n',
1146
 
                          ]
1147
 
                          , list(unified_diff_files('a2', 'b2',
1148
 
                                 sequencematcher=psm)))
1149
 
 
1150
 
 
1151
 
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1152
 
 
1153
 
    _test_needs_features = [CompiledPatienceDiffFeature]
1154
 
 
1155
 
    def setUp(self):
1156
 
        super(TestPatienceDiffLibFiles_c, self).setUp()
1157
 
        import bzrlib._patiencediff_c
1158
 
        self._PatienceSequenceMatcher = \
1159
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1160
 
 
1161
 
 
1162
 
class TestUsingCompiledIfAvailable(TestCase):
1163
 
 
1164
 
    def test_PatienceSequenceMatcher(self):
1165
 
        if CompiledPatienceDiffFeature.available():
1166
 
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1167
 
            self.assertIs(PatienceSequenceMatcher_c,
1168
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1169
 
        else:
1170
 
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1171
 
            self.assertIs(PatienceSequenceMatcher_py,
1172
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1173
 
 
1174
 
    def test_unique_lcs(self):
1175
 
        if CompiledPatienceDiffFeature.available():
1176
 
            from bzrlib._patiencediff_c import unique_lcs_c
1177
 
            self.assertIs(unique_lcs_c,
1178
 
                          bzrlib.patiencediff.unique_lcs)
1179
 
        else:
1180
 
            from bzrlib._patiencediff_py import unique_lcs_py
1181
 
            self.assertIs(unique_lcs_py,
1182
 
                          bzrlib.patiencediff.unique_lcs)
1183
 
 
1184
 
    def test_recurse_matches(self):
1185
 
        if CompiledPatienceDiffFeature.available():
1186
 
            from bzrlib._patiencediff_c import recurse_matches_c
1187
 
            self.assertIs(recurse_matches_c,
1188
 
                          bzrlib.patiencediff.recurse_matches)
1189
 
        else:
1190
 
            from bzrlib._patiencediff_py import recurse_matches_py
1191
 
            self.assertIs(recurse_matches_py,
1192
 
                          bzrlib.patiencediff.recurse_matches)