/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
1
# Copyright (C) 2005, 2006 Canonical Ltd
1711.2.16 by John Arbash Meinel
test_diff needs a copyright statement
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.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
12
#
1711.2.16 by John Arbash Meinel
test_diff needs a copyright statement
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
1740.2.5 by Aaron Bentley
Merge from bzr.dev
17
import os
3123.6.5 by Aaron Bentley
Symlink to real files if possible
18
import os.path
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
19
from cStringIO import StringIO
1692.8.7 by James Henstridge
changes suggested by John Meinel
20
import errno
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
21
import subprocess
1920.1.3 by John Arbash Meinel
Remove spurious import
22
from tempfile import TemporaryFile
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
23
3146.4.3 by Aaron Bentley
Add missing Symlink requirement
24
from bzrlib import tests
3009.2.9 by Aaron Bentley
Add tests for Differ
25
from bzrlib.diff import (
3123.6.2 by Aaron Bentley
Implement diff --using natively
26
    DiffFromTool,
3009.2.22 by Aaron Bentley
Update names & docstring
27
    DiffPath,
28
    DiffSymlink,
29
    DiffTree,
30
    DiffText,
3123.6.2 by Aaron Bentley
Implement diff --using natively
31
    external_diff,
32
    internal_diff,
33
    show_diff_trees,
3009.2.9 by Aaron Bentley
Add tests for Differ
34
    )
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
35
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
2321.2.5 by Alexander Belchenko
external diff: no need for special code path for win32 (suggested by John Meinel)
36
import bzrlib.osutils as osutils
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
37
import bzrlib.patiencediff
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
38
import bzrlib._patiencediff_py
2592.2.4 by Jonathan Lange
Skip the unicode filename test if the platform doesn't support unicode
39
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
40
                          TestCaseInTempDir, TestSkipped)
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
41
42
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
43
class _CompiledPatienceDiffFeature(Feature):
44
45
    def _probe(self):
46
        try:
47
            import bzrlib._patiencediff_c
48
        except ImportError:
49
            return False
50
        return True
51
52
    def feature_name(self):
53
        return 'bzrlib._patiencediff_c'
54
55
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
56
57
2592.2.4 by Jonathan Lange
Skip the unicode filename test if the platform doesn't support unicode
58
class _UnicodeFilename(Feature):
59
    """Does the filesystem support Unicode filenames?"""
60
61
    def _probe(self):
62
        try:
2653.1.1 by John Arbash Meinel
(Jonathan Lange) Fix bug #110092, when displaying a diff that includes a binary file, make sure the binary filenames are encoded
63
            os.stat(u'\u03b1')
2592.2.4 by Jonathan Lange
Skip the unicode filename test if the platform doesn't support unicode
64
        except UnicodeEncodeError:
65
            return False
2592.2.5 by Jonathan Lange
Make UnicodeFilename feature less insane. Add a simple test for it too.
66
        except (IOError, OSError):
67
            # The filesystem allows the Unicode filename but the file doesn't
2653.1.1 by John Arbash Meinel
(Jonathan Lange) Fix bug #110092, when displaying a diff that includes a binary file, make sure the binary filenames are encoded
68
            # exist.
2592.2.5 by Jonathan Lange
Make UnicodeFilename feature less insane. Add a simple test for it too.
69
            return True
70
        else:
71
            # The filesystem allows the Unicode filename and the file exists,
72
            # for some reason.
73
            return True
2592.2.4 by Jonathan Lange
Skip the unicode filename test if the platform doesn't support unicode
74
75
UnicodeFilename = _UnicodeFilename()
76
77
2592.2.5 by Jonathan Lange
Make UnicodeFilename feature less insane. Add a simple test for it too.
78
class TestUnicodeFilename(TestCase):
79
80
    def test_probe_passes(self):
81
        """UnicodeFilename._probe passes."""
82
        # We can't test much more than that because the behaviour depends
83
        # on the platform.
84
        UnicodeFilename._probe()
85
        
86
1558.15.11 by Aaron Bentley
Apply merge review suggestions
87
def udiff_lines(old, new, allow_binary=False):
974.1.6 by Aaron Bentley
Added unit tests
88
    output = StringIO()
1558.15.11 by Aaron Bentley
Apply merge review suggestions
89
    internal_diff('old', old, 'new', new, output, allow_binary)
974.1.6 by Aaron Bentley
Added unit tests
90
    output.seek(0, 0)
91
    return output.readlines()
92
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
93
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
94
def external_udiff_lines(old, new, use_stringio=False):
95
    if use_stringio:
96
        # StringIO has no fileno, so it tests a different codepath
97
        output = StringIO()
98
    else:
99
        output = TemporaryFile()
1692.8.7 by James Henstridge
changes suggested by John Meinel
100
    try:
101
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
1711.2.58 by John Arbash Meinel
Use osutils.pumpfile so we don't have to buffer everything in ram
102
    except NoDiff:
1711.2.56 by John Arbash Meinel
Raise NoDiff if 'diff' not present.
103
        raise TestSkipped('external "diff" not present to test')
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
104
    output.seek(0, 0)
105
    lines = output.readlines()
106
    output.close()
107
    return lines
108
109
1102 by Martin Pool
- merge test refactoring from robertc
110
class TestDiff(TestCase):
1185.81.25 by Aaron Bentley
Clean up test_diff
111
1102 by Martin Pool
- merge test refactoring from robertc
112
    def test_add_nl(self):
113
        """diff generates a valid diff for patches that add a newline"""
974.1.6 by Aaron Bentley
Added unit tests
114
        lines = udiff_lines(['boo'], ['boo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
115
        self.check_patch(lines)
116
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
117
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
118
1102 by Martin Pool
- merge test refactoring from robertc
119
    def test_add_nl_2(self):
120
        """diff generates a valid diff for patches that change last line and
121
        add a newline.
122
        """
974.1.6 by Aaron Bentley
Added unit tests
123
        lines = udiff_lines(['boo'], ['goo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
124
        self.check_patch(lines)
125
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
126
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
127
1102 by Martin Pool
- merge test refactoring from robertc
128
    def test_remove_nl(self):
129
        """diff generates a valid diff for patches that change last line and
130
        add a newline.
131
        """
974.1.6 by Aaron Bentley
Added unit tests
132
        lines = udiff_lines(['boo\n'], ['boo'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
133
        self.check_patch(lines)
134
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
135
            ## "expected no-nl, got %r" % lines[5]
136
137
    def check_patch(self, lines):
138
        self.assert_(len(lines) > 1)
139
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
140
        self.assert_(lines[0].startswith ('---'))
141
            ## 'No orig line for patch:\n%s' % "".join(lines)
142
        self.assert_(lines[1].startswith ('+++'))
143
            ## 'No mod line for patch:\n%s' % "".join(lines)
144
        self.assert_(len(lines) > 2)
145
            ## "No hunks for patch:\n%s" % "".join(lines)
146
        self.assert_(lines[2].startswith('@@'))
147
            ## "No hunk header for patch:\n%s" % "".join(lines)
148
        self.assert_('@@' in lines[2][2:])
149
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
150
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
151
    def test_binary_lines(self):
152
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
153
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
1558.15.11 by Aaron Bentley
Apply merge review suggestions
154
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
155
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
156
157
    def test_external_diff(self):
158
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
159
        self.check_patch(lines)
1899.1.6 by John Arbash Meinel
internal_diff always adds a trailing \n, make sure external_diff does too
160
        self.assertEqual('\n', lines[-1])
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
161
162
    def test_external_diff_no_fileno(self):
163
        # Make sure that we can handle not having a fileno, even
164
        # if the diff is large
165
        lines = external_udiff_lines(['boo\n']*10000,
166
                                     ['goo\n']*10000,
167
                                     use_stringio=True)
168
        self.check_patch(lines)
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
169
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
170
    def test_external_diff_binary_lang_c(self):
2321.2.5 by Alexander Belchenko
external diff: no need for special code path for win32 (suggested by John Meinel)
171
        old_env = {}
172
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
173
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
174
        try:
175
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
1959.1.1 by Marien Zwart
merge.
176
            # Older versions of diffutils say "Binary files", newer
177
            # versions just say "Files".
178
            self.assertContainsRe(lines[0],
179
                                  '(Binary f|F)iles old and new differ\n')
180
            self.assertEquals(lines[1:], ['\n'])
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
181
        finally:
2321.2.5 by Alexander Belchenko
external diff: no need for special code path for win32 (suggested by John Meinel)
182
            for lang, old_val in old_env.iteritems():
183
                osutils.set_or_unset_env(lang, old_val)
1899.1.4 by John Arbash Meinel
Just swallow a return code of 2
184
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
185
    def test_no_external_diff(self):
186
        """Check that NoDiff is raised when diff is not available"""
187
        # Use os.environ['PATH'] to make sure no 'diff' command is available
188
        orig_path = os.environ['PATH']
189
        try:
190
            os.environ['PATH'] = ''
191
            self.assertRaises(NoDiff, external_diff,
192
                              'old', ['boo\n'], 'new', ['goo\n'],
193
                              StringIO(), diff_opts=['-u'])
194
        finally:
195
            os.environ['PATH'] = orig_path
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
196
        
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
197
    def test_internal_diff_default(self):
198
        # Default internal diff encoding is utf8
199
        output = StringIO()
200
        internal_diff(u'old_\xb5', ['old_text\n'],
201
                    u'new_\xe5', ['new_text\n'], output)
202
        lines = output.getvalue().splitlines(True)
203
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
204
        self.assertEquals(['--- old_\xc2\xb5\n',
205
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
206
                           '@@ -1,1 +1,1 @@\n',
207
                           '-old_text\n',
208
                           '+new_text\n',
209
                           '\n',
210
                          ]
211
                          , lines)
212
213
    def test_internal_diff_utf8(self):
214
        output = StringIO()
215
        internal_diff(u'old_\xb5', ['old_text\n'],
216
                    u'new_\xe5', ['new_text\n'], output,
217
                    path_encoding='utf8')
218
        lines = output.getvalue().splitlines(True)
219
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
220
        self.assertEquals(['--- old_\xc2\xb5\n',
221
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
222
                           '@@ -1,1 +1,1 @@\n',
223
                           '-old_text\n',
224
                           '+new_text\n',
225
                           '\n',
226
                          ]
227
                          , lines)
228
229
    def test_internal_diff_iso_8859_1(self):
230
        output = StringIO()
231
        internal_diff(u'old_\xb5', ['old_text\n'],
232
                    u'new_\xe5', ['new_text\n'], output,
233
                    path_encoding='iso-8859-1')
234
        lines = output.getvalue().splitlines(True)
235
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
236
        self.assertEquals(['--- old_\xb5\n',
237
                           '+++ new_\xe5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
238
                           '@@ -1,1 +1,1 @@\n',
239
                           '-old_text\n',
240
                           '+new_text\n',
241
                           '\n',
242
                          ]
243
                          , lines)
244
3085.1.1 by John Arbash Meinel
Fix internal_diff to not fail when the texts are identical.
245
    def test_internal_diff_no_content(self):
246
        output = StringIO()
247
        internal_diff(u'old', [], u'new', [], output)
248
        self.assertEqual('', output.getvalue())
249
250
    def test_internal_diff_no_changes(self):
251
        output = StringIO()
252
        internal_diff(u'old', ['text\n', 'contents\n'],
253
                      u'new', ['text\n', 'contents\n'],
254
                      output)
255
        self.assertEqual('', output.getvalue())
256
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
257
    def test_internal_diff_returns_bytes(self):
258
        import StringIO
259
        output = StringIO.StringIO()
260
        internal_diff(u'old_\xb5', ['old_text\n'],
261
                    u'new_\xe5', ['new_text\n'], output)
262
        self.failUnless(isinstance(output.getvalue(), str),
263
            'internal_diff should return bytestrings')
264
1185.81.25 by Aaron Bentley
Clean up test_diff
265
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
266
class TestDiffFiles(TestCaseInTempDir):
267
268
    def test_external_diff_binary(self):
269
        """The output when using external diff should use diff's i18n error"""
270
        # Make sure external_diff doesn't fail in the current LANG
271
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
272
2240.1.1 by Alexander Belchenko
test_external_diff_binary: run external diff with --binary flag
273
        cmd = ['diff', '-u', '--binary', 'old', 'new']
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
274
        open('old', 'wb').write('\x00foobar\n')
275
        open('new', 'wb').write('foo\x00bar\n')
276
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
277
                                     stdin=subprocess.PIPE)
278
        out, err = pipe.communicate()
279
        # Diff returns '2' on Binary files.
280
        self.assertEqual(2, pipe.returncode)
281
        # We should output whatever diff tells us, plus a trailing newline
282
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
283
284
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
285
class TestShowDiffTreesHelper(TestCaseWithTransport):
286
    """Has a helper for running show_diff_trees"""
287
288
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
289
        output = StringIO()
290
        if working_tree is not None:
291
            extra_trees = (working_tree,)
292
        else:
293
            extra_trees = ()
294
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
295
                        extra_trees=extra_trees, old_label='old/',
296
                        new_label='new/')
297
        return output.getvalue()
298
299
300
class TestDiffDates(TestShowDiffTreesHelper):
1740.2.5 by Aaron Bentley
Merge from bzr.dev
301
302
    def setUp(self):
303
        super(TestDiffDates, self).setUp()
304
        self.wt = self.make_branch_and_tree('.')
305
        self.b = self.wt.branch
306
        self.build_tree_contents([
307
            ('file1', 'file1 contents at rev 1\n'),
308
            ('file2', 'file2 contents at rev 1\n')
309
            ])
310
        self.wt.add(['file1', 'file2'])
311
        self.wt.commit(
312
            message='Revision 1',
313
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
314
            timezone=0,
315
            rev_id='rev-1')
316
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
317
        self.wt.commit(
318
            message='Revision 2',
319
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
320
            timezone=28800,
321
            rev_id='rev-2')
322
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
323
        self.wt.commit(
324
            message='Revision 3',
325
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
326
            timezone=-3600,
327
            rev_id='rev-3')
328
        self.wt.remove(['file2'])
329
        self.wt.commit(
330
            message='Revision 4',
331
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
332
            timezone=0,
333
            rev_id='rev-4')
334
        self.build_tree_contents([
335
            ('file1', 'file1 contents in working tree\n')
336
            ])
337
        # set the date stamps for files in the working tree to known values
338
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
339
340
    def test_diff_rev_tree_working_tree(self):
341
        output = self.get_diff(self.wt.basis_tree(), self.wt)
342
        # note that the date for old/file1 is from rev 2 rather than from
343
        # the basis revision (rev 4)
344
        self.assertEqualDiff(output, '''\
345
=== modified file 'file1'
346
--- old/file1\t2006-04-02 00:00:00 +0000
347
+++ new/file1\t2006-04-05 00:00:00 +0000
348
@@ -1,1 +1,1 @@
349
-file1 contents at rev 2
350
+file1 contents in working tree
351
352
''')
353
354
    def test_diff_rev_tree_rev_tree(self):
355
        tree1 = self.b.repository.revision_tree('rev-2')
356
        tree2 = self.b.repository.revision_tree('rev-3')
357
        output = self.get_diff(tree1, tree2)
358
        self.assertEqualDiff(output, '''\
359
=== modified file 'file2'
360
--- old/file2\t2006-04-01 00:00:00 +0000
361
+++ new/file2\t2006-04-03 00:00:00 +0000
362
@@ -1,1 +1,1 @@
363
-file2 contents at rev 1
364
+file2 contents at rev 3
365
366
''')
367
        
368
    def test_diff_add_files(self):
369
        tree1 = self.b.repository.revision_tree(None)
370
        tree2 = self.b.repository.revision_tree('rev-1')
371
        output = self.get_diff(tree1, tree2)
372
        # the files have the epoch time stamp for the tree in which
373
        # they don't exist.
374
        self.assertEqualDiff(output, '''\
375
=== added file 'file1'
376
--- old/file1\t1970-01-01 00:00:00 +0000
377
+++ new/file1\t2006-04-01 00:00:00 +0000
378
@@ -0,0 +1,1 @@
379
+file1 contents at rev 1
380
381
=== added file 'file2'
382
--- old/file2\t1970-01-01 00:00:00 +0000
383
+++ new/file2\t2006-04-01 00:00:00 +0000
384
@@ -0,0 +1,1 @@
385
+file2 contents at rev 1
386
387
''')
388
389
    def test_diff_remove_files(self):
390
        tree1 = self.b.repository.revision_tree('rev-3')
391
        tree2 = self.b.repository.revision_tree('rev-4')
392
        output = self.get_diff(tree1, tree2)
393
        # the file has the epoch time stamp for the tree in which
394
        # it doesn't exist.
395
        self.assertEqualDiff(output, '''\
396
=== removed file 'file2'
397
--- old/file2\t2006-04-03 00:00:00 +0000
398
+++ new/file2\t1970-01-01 00:00:00 +0000
399
@@ -1,1 +0,0 @@
400
-file2 contents at rev 3
401
402
''')
403
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
404
    def test_show_diff_specified(self):
1551.7.22 by Aaron Bentley
Changes from review
405
        """A working tree filename can be used to identify a file"""
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
406
        self.wt.rename_one('file1', 'file1b')
407
        old_tree = self.b.repository.revision_tree('rev-1')
408
        new_tree = self.b.repository.revision_tree('rev-4')
1551.7.22 by Aaron Bentley
Changes from review
409
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
410
                            working_tree=self.wt)
411
        self.assertContainsRe(out, 'file1\t')
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
412
1551.7.22 by Aaron Bentley
Changes from review
413
    def test_recursive_diff(self):
414
        """Children of directories are matched"""
415
        os.mkdir('dir1')
416
        os.mkdir('dir2')
417
        self.wt.add(['dir1', 'dir2'])
418
        self.wt.rename_one('file1', 'dir1/file1')
419
        old_tree = self.b.repository.revision_tree('rev-1')
420
        new_tree = self.b.repository.revision_tree('rev-4')
421
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
422
                            working_tree=self.wt)
423
        self.assertContainsRe(out, 'file1\t')
424
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
425
                            working_tree=self.wt)
426
        self.assertNotContainsRe(out, 'file1\t')
1740.2.5 by Aaron Bentley
Merge from bzr.dev
427
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
428
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
429
430
class TestShowDiffTrees(TestShowDiffTreesHelper):
431
    """Direct tests for show_diff_trees"""
432
433
    def test_modified_file(self):
434
        """Test when a file is modified."""
435
        tree = self.make_branch_and_tree('tree')
436
        self.build_tree_contents([('tree/file', 'contents\n')])
437
        tree.add(['file'], ['file-id'])
438
        tree.commit('one', rev_id='rev-1')
439
440
        self.build_tree_contents([('tree/file', 'new contents\n')])
441
        diff = self.get_diff(tree.basis_tree(), tree)
442
        self.assertContainsRe(diff, "=== modified file 'file'\n")
443
        self.assertContainsRe(diff, '--- old/file\t')
444
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
445
        self.assertContainsRe(diff, '-contents\n'
446
                                    '\\+new contents\n')
447
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
448
    def test_modified_file_in_renamed_dir(self):
449
        """Test when a file is modified in a renamed directory."""
450
        tree = self.make_branch_and_tree('tree')
451
        self.build_tree(['tree/dir/'])
452
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
453
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
454
        tree.commit('one', rev_id='rev-1')
455
456
        tree.rename_one('dir', 'other')
457
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
458
        diff = self.get_diff(tree.basis_tree(), tree)
459
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
460
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
461
        # XXX: This is technically incorrect, because it used to be at another
462
        # location. What to do?
2405.1.3 by John Arbash Meinel
Do a better fix, which recognizes that we should pass the correct old path.
463
        self.assertContainsRe(diff, '--- old/dir/file\t')
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
464
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
465
        self.assertContainsRe(diff, '-contents\n'
466
                                    '\\+new contents\n')
467
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
468
    def test_renamed_directory(self):
469
        """Test when only a directory is only renamed."""
470
        tree = self.make_branch_and_tree('tree')
471
        self.build_tree(['tree/dir/'])
472
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
473
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
474
        tree.commit('one', rev_id='rev-1')
475
476
        tree.rename_one('dir', 'newdir')
477
        diff = self.get_diff(tree.basis_tree(), tree)
478
        # Renaming a directory should be a single "you renamed this dir" even
479
        # when there are files inside.
480
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
481
482
    def test_renamed_file(self):
483
        """Test when a file is only renamed."""
484
        tree = self.make_branch_and_tree('tree')
485
        self.build_tree_contents([('tree/file', 'contents\n')])
486
        tree.add(['file'], ['file-id'])
487
        tree.commit('one', rev_id='rev-1')
488
489
        tree.rename_one('file', 'newname')
490
        diff = self.get_diff(tree.basis_tree(), tree)
491
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
492
        # We shouldn't have a --- or +++ line, because there is no content
493
        # change
494
        self.assertNotContainsRe(diff, '---')
495
496
    def test_renamed_and_modified_file(self):
497
        """Test when a file is only renamed."""
498
        tree = self.make_branch_and_tree('tree')
499
        self.build_tree_contents([('tree/file', 'contents\n')])
500
        tree.add(['file'], ['file-id'])
501
        tree.commit('one', rev_id='rev-1')
502
503
        tree.rename_one('file', 'newname')
504
        self.build_tree_contents([('tree/newname', 'new contents\n')])
505
        diff = self.get_diff(tree.basis_tree(), tree)
506
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
507
        self.assertContainsRe(diff, '--- old/file\t')
508
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
509
        self.assertContainsRe(diff, '-contents\n'
510
                                    '\\+new contents\n')
511
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
512
    def test_binary_unicode_filenames(self):
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
513
        """Test that contents of files are *not* encoded in UTF-8 when there
514
        is a binary file in the diff.
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
515
        """
516
        # See https://bugs.launchpad.net/bugs/110092.
2592.2.4 by Jonathan Lange
Skip the unicode filename test if the platform doesn't support unicode
517
        self.requireFeature(UnicodeFilename)
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
518
519
        # This bug isn't triggered with cStringIO.
520
        from StringIO import StringIO
521
        tree = self.make_branch_and_tree('tree')
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
522
        alpha, omega = u'\u03b1', u'\u03c9'
523
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
524
        self.build_tree_contents(
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
525
            [('tree/' + alpha, chr(0)),
526
             ('tree/' + omega,
527
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
528
        tree.add([alpha], ['file-id'])
529
        tree.add([omega], ['file-id-2'])
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
530
        diff_content = StringIO()
531
        show_diff_trees(tree.basis_tree(), tree, diff_content)
532
        diff = diff_content.getvalue()
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
533
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
534
        self.assertContainsRe(
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
535
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
536
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
537
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
538
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
539
2725.2.1 by ghigo
When a unicode filename is renamed, in the diff is showed a wrong result
540
    def test_unicode_filename(self):
541
        """Test when the filename are unicode."""
542
        self.requireFeature(UnicodeFilename)
543
544
        alpha, omega = u'\u03b1', u'\u03c9'
545
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
546
547
        tree = self.make_branch_and_tree('tree')
548
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
549
        tree.add(['ren_'+alpha], ['file-id-2'])
550
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
551
        tree.add(['del_'+alpha], ['file-id-3'])
552
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
553
        tree.add(['mod_'+alpha], ['file-id-4'])
554
555
        tree.commit('one', rev_id='rev-1')
556
557
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
558
        tree.remove('del_'+alpha)
559
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
560
        tree.add(['add_'+alpha], ['file-id'])
561
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
562
563
        diff = self.get_diff(tree.basis_tree(), tree)
564
        self.assertContainsRe(diff,
565
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
566
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
567
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
568
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
569
3009.2.9 by Aaron Bentley
Add tests for Differ
570
3009.2.22 by Aaron Bentley
Update names & docstring
571
class DiffWasIs(DiffPath):
3009.2.15 by Aaron Bentley
Test differ registration
572
573
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
574
        self.to_file.write('was: ')
575
        self.to_file.write(self.old_tree.get_file(file_id).read())
576
        self.to_file.write('is: ')
577
        self.to_file.write(self.new_tree.get_file(file_id).read())
578
        pass
579
580
3009.2.22 by Aaron Bentley
Update names & docstring
581
class TestDiffTree(TestCaseWithTransport):
3009.2.9 by Aaron Bentley
Add tests for Differ
582
583
    def setUp(self):
584
        TestCaseWithTransport.setUp(self)
585
        self.old_tree = self.make_branch_and_tree('old-tree')
586
        self.old_tree.lock_write()
587
        self.addCleanup(self.old_tree.unlock)
588
        self.new_tree = self.make_branch_and_tree('new-tree')
589
        self.new_tree.lock_write()
590
        self.addCleanup(self.new_tree.unlock)
3009.2.22 by Aaron Bentley
Update names & docstring
591
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
3009.2.9 by Aaron Bentley
Add tests for Differ
592
593
    def test_diff_text(self):
594
        self.build_tree_contents([('old-tree/olddir/',),
595
                                  ('old-tree/olddir/oldfile', 'old\n')])
596
        self.old_tree.add('olddir')
597
        self.old_tree.add('olddir/oldfile', 'file-id')
598
        self.build_tree_contents([('new-tree/newdir/',),
599
                                  ('new-tree/newdir/newfile', 'new\n')])
600
        self.new_tree.add('newdir')
601
        self.new_tree.add('newdir/newfile', 'file-id')
3009.2.22 by Aaron Bentley
Update names & docstring
602
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
603
        differ.diff_text('file-id', None, 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
604
        self.assertEqual(
605
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
606
            differ.to_file.getvalue())
607
        differ.to_file.seek(0)
608
        differ.diff_text(None, 'file-id', 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
609
        self.assertEqual(
610
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
611
            differ.to_file.getvalue())
612
        differ.to_file.seek(0)
613
        differ.diff_text('file-id', 'file-id', 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
614
        self.assertEqual(
615
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
616
            differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
617
3087.1.1 by Aaron Bentley
Diff handles missing files correctly, with no tracebacks
618
    def test_diff_deletion(self):
619
        self.build_tree_contents([('old-tree/file', 'contents'),
620
                                  ('new-tree/file', 'contents')])
621
        self.old_tree.add('file', 'file-id')
622
        self.new_tree.add('file', 'file-id')
623
        os.unlink('new-tree/file')
624
        self.differ.show_diff(None)
625
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
626
627
    def test_diff_creation(self):
628
        self.build_tree_contents([('old-tree/file', 'contents'),
629
                                  ('new-tree/file', 'contents')])
630
        self.old_tree.add('file', 'file-id')
631
        self.new_tree.add('file', 'file-id')
632
        os.unlink('old-tree/file')
633
        self.differ.show_diff(None)
634
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
635
3009.2.9 by Aaron Bentley
Add tests for Differ
636
    def test_diff_symlink(self):
3009.2.22 by Aaron Bentley
Update names & docstring
637
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
638
        differ.diff_symlink('old target', None)
3009.2.9 by Aaron Bentley
Add tests for Differ
639
        self.assertEqual("=== target was 'old target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
640
                         differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
641
3009.2.22 by Aaron Bentley
Update names & docstring
642
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
643
        differ.diff_symlink(None, 'new target')
3009.2.9 by Aaron Bentley
Add tests for Differ
644
        self.assertEqual("=== target is 'new target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
645
                         differ.to_file.getvalue())
646
3009.2.22 by Aaron Bentley
Update names & docstring
647
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
648
        differ.diff_symlink('old target', 'new target')
3009.2.9 by Aaron Bentley
Add tests for Differ
649
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
650
                         differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
651
652
    def test_diff(self):
653
        self.build_tree_contents([('old-tree/olddir/',),
654
                                  ('old-tree/olddir/oldfile', 'old\n')])
655
        self.old_tree.add('olddir')
656
        self.old_tree.add('olddir/oldfile', 'file-id')
657
        self.build_tree_contents([('new-tree/newdir/',),
658
                                  ('new-tree/newdir/newfile', 'new\n')])
659
        self.new_tree.add('newdir')
660
        self.new_tree.add('newdir/newfile', 'file-id')
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
661
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
3009.2.9 by Aaron Bentley
Add tests for Differ
662
        self.assertContainsRe(
663
            self.differ.to_file.getvalue(),
664
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
665
             ' \@\@\n-old\n\+new\n\n')
666
667
    def test_diff_kind_change(self):
3146.4.3 by Aaron Bentley
Add missing Symlink requirement
668
        self.requireFeature(tests.SymlinkFeature)
3009.2.9 by Aaron Bentley
Add tests for Differ
669
        self.build_tree_contents([('old-tree/olddir/',),
670
                                  ('old-tree/olddir/oldfile', 'old\n')])
671
        self.old_tree.add('olddir')
672
        self.old_tree.add('olddir/oldfile', 'file-id')
673
        self.build_tree(['new-tree/newdir/'])
674
        os.symlink('new', 'new-tree/newdir/newfile')
675
        self.new_tree.add('newdir')
676
        self.new_tree.add('newdir/newfile', 'file-id')
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
677
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
3009.2.9 by Aaron Bentley
Add tests for Differ
678
        self.assertContainsRe(
679
            self.differ.to_file.getvalue(),
680
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
681
             ' \@\@\n-old\n\n')
682
        self.assertContainsRe(self.differ.to_file.getvalue(),
683
                              "=== target is 'new'\n")
684
3009.2.19 by Aaron Bentley
Implement directory diffing
685
    def test_diff_directory(self):
686
        self.build_tree(['new-tree/new-dir/'])
687
        self.new_tree.add('new-dir', 'new-dir-id')
688
        self.differ.diff('new-dir-id', None, 'new-dir')
689
        self.assertEqual(self.differ.to_file.getvalue(), '')
690
3009.2.16 by Aaron Bentley
Test support for extra differs
691
    def create_old_new(self):
692
        self.build_tree_contents([('old-tree/olddir/',),
693
                                  ('old-tree/olddir/oldfile', 'old\n')])
694
        self.old_tree.add('olddir')
695
        self.old_tree.add('olddir/oldfile', 'file-id')
696
        self.build_tree_contents([('new-tree/newdir/',),
697
                                  ('new-tree/newdir/newfile', 'new\n')])
698
        self.new_tree.add('newdir')
699
        self.new_tree.add('newdir/newfile', 'file-id')
700
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
701
    def test_register_diff(self):
3009.2.16 by Aaron Bentley
Test support for extra differs
702
        self.create_old_new()
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
703
        old_diff_factories = DiffTree.diff_factories
704
        DiffTree.diff_factories=old_diff_factories[:]
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
705
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
3009.2.16 by Aaron Bentley
Test support for extra differs
706
        try:
3009.2.22 by Aaron Bentley
Update names & docstring
707
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
3009.2.16 by Aaron Bentley
Test support for extra differs
708
        finally:
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
709
            DiffTree.diff_factories = old_diff_factories
3009.2.16 by Aaron Bentley
Test support for extra differs
710
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
711
        self.assertNotContainsRe(
712
            differ.to_file.getvalue(),
713
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
714
             ' \@\@\n-old\n\+new\n\n')
715
        self.assertContainsRe(differ.to_file.getvalue(),
716
                              'was: old\nis: new\n')
717
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
718
    def test_extra_factories(self):
3009.2.16 by Aaron Bentley
Test support for extra differs
719
        self.create_old_new()
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
720
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
721
                            extra_factories=[DiffWasIs.from_diff_tree])
3009.2.16 by Aaron Bentley
Test support for extra differs
722
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
723
        self.assertNotContainsRe(
724
            differ.to_file.getvalue(),
725
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
726
             ' \@\@\n-old\n\+new\n\n')
727
        self.assertContainsRe(differ.to_file.getvalue(),
728
                              'was: old\nis: new\n')
729
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
730
    def test_alphabetical_order(self):
731
        self.build_tree(['new-tree/a-file'])
732
        self.new_tree.add('a-file')
733
        self.build_tree(['old-tree/b-file'])
734
        self.old_tree.add('b-file')
735
        self.differ.show_diff(None)
736
        self.assertContainsRe(self.differ.to_file.getvalue(),
737
            '.*a-file(.|\n)*b-file')
738
3009.2.9 by Aaron Bentley
Add tests for Differ
739
1711.2.15 by John Arbash Meinel
Found a couple CDV left
740
class TestPatienceDiffLib(TestCase):
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
741
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
742
    def setUp(self):
743
        super(TestPatienceDiffLib, self).setUp()
744
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
745
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
746
        self._PatienceSequenceMatcher = \
747
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
748
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
749
    def test_unique_lcs(self):
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
750
        unique_lcs = self._unique_lcs
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
751
        self.assertEquals(unique_lcs('', ''), [])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
752
        self.assertEquals(unique_lcs('', 'a'), [])
753
        self.assertEquals(unique_lcs('a', ''), [])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
754
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
755
        self.assertEquals(unique_lcs('a', 'b'), [])
756
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
757
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
758
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
759
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
760
                                                         (3,3), (4,4)])
761
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
762
763
    def test_recurse_matches(self):
764
        def test_one(a, b, matches):
765
            test_matches = []
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
766
            self._recurse_matches(
767
                a, b, 0, 0, len(a), len(b), test_matches, 10)
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
768
            self.assertEquals(test_matches, matches)
769
1711.2.17 by John Arbash Meinel
Small cleanups to patience_diff code.
770
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
771
                 [(0, 0), (2, 2), (4, 4)])
772
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
773
                 [(0, 0), (2, 1), (4, 2)])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
774
        # Even though 'bc' is not unique globally, and is surrounded by
775
        # non-matching lines, we should still match, because they are locally
776
        # unique
777
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
778
                                          (4, 6), (5, 7), (6, 8)])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
779
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
780
        # recurse_matches doesn't match non-unique 
781
        # lines surrounded by bogus text.
1185.81.24 by Aaron Bentley
Reoganize patience-related code
782
        # The update has been done in patiencediff.SequenceMatcher instead
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
783
784
        # This is what it could be
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
785
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
786
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
787
        # This is what it currently gives:
788
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
789
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
790
    def assertDiffBlocks(self, a, b, expected_blocks):
791
        """Check that the sequence matcher returns the correct blocks.
792
793
        :param a: A sequence to match
794
        :param b: Another sequence to match
795
        :param expected_blocks: The expected output, not including the final
796
            matching block (len(a), len(b), 0)
797
        """
798
        matcher = self._PatienceSequenceMatcher(None, a, b)
799
        blocks = matcher.get_matching_blocks()
800
        last = blocks.pop()
801
        self.assertEqual((len(a), len(b), 0), last)
802
        self.assertEqual(expected_blocks, blocks)
803
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
804
    def test_matching_blocks(self):
1185.81.2 by John Arbash Meinel
A couple small tests.
805
        # Some basic matching tests
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
806
        self.assertDiffBlocks('', '', [])
807
        self.assertDiffBlocks([], [], [])
808
        self.assertDiffBlocks('abc', '', [])
809
        self.assertDiffBlocks('', 'abc', [])
810
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
811
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
812
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
813
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
814
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
815
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
816
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
817
        # This may check too much, but it checks to see that
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
818
        # a copied block stays attached to the previous section,
819
        # not the later one.
820
        # difflib would tend to grab the trailing longest match
821
        # which would make the diff not look right
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
822
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
823
                              [(0, 0, 6), (6, 11, 10)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
824
1185.81.2 by John Arbash Meinel
A couple small tests.
825
        # make sure it supports passing in lists
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
826
        self.assertDiffBlocks(
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
827
                   ['hello there\n',
828
                    'world\n',
829
                    'how are you today?\n'],
830
                   ['hello there\n',
831
                    'how are you today?\n'],
1185.81.2 by John Arbash Meinel
A couple small tests.
832
                [(0, 0, 1), (2, 1, 1)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
833
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
834
        # non unique lines surrounded by non-matching lines
835
        # won't be found
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
836
        self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
837
838
        # But they only need to be locally unique
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
839
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
840
841
        # non unique blocks won't be matched
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
842
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
843
844
        # but locally unique ones will
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
845
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
846
                                              (5,4,1), (7,5,2), (10,8,1)])
847
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
848
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
849
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
850
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
1185.81.11 by John Arbash Meinel
Found some edge cases that weren't being matched.
851
3074.2.1 by John Arbash Meinel
Change the C PatienceDiff implementation to support arbitrary objects.
852
    def test_matching_blocks_tuples(self):
853
        # Some basic matching tests
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
854
        self.assertDiffBlocks([], [], [])
855
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
856
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
857
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
858
                              [('a',), ('b',), ('c,')],
859
                              [(0, 0, 3)])
860
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
861
                              [('a',), ('b',), ('d,')],
862
                              [(0, 0, 2)])
863
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
864
                              [('a',), ('b',), ('c,')],
865
                              [(1, 1, 2)])
866
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
867
                              [('a',), ('b',), ('c,')],
868
                              [(1, 0, 3)])
869
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
870
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
871
                              [(0, 0, 1), (2, 2, 1)])
872
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
873
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
874
                              [(0, 0, 1), (2, 2, 1)])
3074.2.1 by John Arbash Meinel
Change the C PatienceDiff implementation to support arbitrary objects.
875
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
876
    def test_opcodes(self):
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
877
        def chk_ops(a, b, expected_codes):
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
878
            s = self._PatienceSequenceMatcher(None, a, b)
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
879
            self.assertEquals(expected_codes, s.get_opcodes())
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
880
881
        chk_ops('', '', [])
882
        chk_ops([], [], [])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
883
        chk_ops('abc', '', [('delete', 0,3, 0,0)])
884
        chk_ops('', 'abc', [('insert', 0,0, 0,3)])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
885
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
886
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
887
                                 ('replace', 3,4, 3,4)
888
                                ])
889
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
890
                                 ('equal',  1,4, 0,3),
891
                                 ('insert', 4,4, 3,4)
892
                                ])
893
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
894
                                  ('equal',  1,5, 0,4)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
895
                                 ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
896
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
897
                                   ('replace', 2,3, 2,3),
898
                                   ('equal',   3,5, 3,5)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
899
                                  ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
900
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
901
                                     ('replace', 2,3, 2,5),
902
                                     ('equal',   3,5, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
903
                                    ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
904
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
905
                                    ('insert', 2,2, 2,5),
906
                                    ('equal',  2,4, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
907
                                   ])
908
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
909
                [('equal',  0,6,  0,6),
910
                 ('insert', 6,6,  6,11),
911
                 ('equal',  6,16, 11,21)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
912
                ])
913
        chk_ops(
914
                [ 'hello there\n'
915
                , 'world\n'
916
                , 'how are you today?\n'],
917
                [ 'hello there\n'
918
                , 'how are you today?\n'],
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
919
                [('equal',  0,1, 0,1),
920
                 ('delete', 1,2, 1,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
921
                 ('equal',  2,3, 1,2),
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
922
                ])
923
        chk_ops('aBccDe', 'abccde', 
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
924
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
925
                 ('replace', 1,5, 1,5),
926
                 ('equal',   5,6, 5,6),
927
                ])
928
        chk_ops('aBcDec', 'abcdec', 
929
                [('equal',   0,1, 0,1),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
930
                 ('replace', 1,2, 1,2),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
931
                 ('equal',   2,3, 2,3),
932
                 ('replace', 3,4, 3,4),
933
                 ('equal',   4,6, 4,6),
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
934
                ])
1185.81.10 by John Arbash Meinel
Added some more test cases.
935
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
936
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
937
                 ('replace', 1,8, 1,8),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
938
                 ('equal',   8,9, 8,9)
1185.81.10 by John Arbash Meinel
Added some more test cases.
939
                ])
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
940
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
941
                [('equal',   0,1, 0,1),
942
                 ('replace', 1,2, 1,2),
943
                 ('equal',   2,4, 2,4),
944
                 ('delete', 4,5, 4,4),
945
                 ('equal',   5,6, 4,5),
946
                 ('delete', 6,7, 5,5),
947
                 ('equal',   7,9, 5,7),
948
                 ('replace', 9,10, 7,8),
949
                 ('equal',   10,11, 8,9)
950
                ])
1185.81.10 by John Arbash Meinel
Added some more test cases.
951
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
952
    def test_grouped_opcodes(self):
953
        def chk_ops(a, b, expected_codes, n=3):
954
            s = self._PatienceSequenceMatcher(None, a, b)
955
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
956
957
        chk_ops('', '', [])
958
        chk_ops([], [], [])
959
        chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
960
        chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
961
        chk_ops('abcd', 'abcd', [])
962
        chk_ops('abcd', 'abce', [[('equal',   0,3, 0,3),
963
                                  ('replace', 3,4, 3,4)
964
                                 ]])
965
        chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
966
                                 ('equal',  1,4, 0,3),
967
                                 ('insert', 4,4, 3,4)
968
                                ]])
969
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
970
                [[('equal',  3,6, 3,6),
971
                  ('insert', 6,6, 6,11),
972
                  ('equal',  6,9, 11,14)
973
                  ]])
974
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
975
                [[('equal',  2,6, 2,6),
976
                  ('insert', 6,6, 6,11),
977
                  ('equal',  6,10, 11,15)
978
                  ]], 4)
979
        chk_ops('Xabcdef', 'abcdef',
980
                [[('delete', 0,1, 0,0),
981
                  ('equal',  1,4, 0,3)
982
                  ]])
983
        chk_ops('abcdef', 'abcdefX',
984
                [[('equal',  3,6, 3,6),
985
                  ('insert', 6,6, 6,7)
986
                  ]])
987
988
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
989
    def test_multiple_ranges(self):
990
        # There was an earlier bug where we used a bad set of ranges,
991
        # this triggers that specific bug, to make sure it doesn't regress
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
992
        self.assertDiffBlocks('abcdefghijklmnop',
993
                              'abcXghiYZQRSTUVWXYZijklmnop',
994
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
995
996
        self.assertDiffBlocks('ABCd efghIjk  L',
997
                              'AxyzBCn mo pqrstuvwI1 2  L',
998
                              [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
999
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1000
        # These are rot13 code snippets.
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1001
        self.assertDiffBlocks('''\
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1002
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1003
    """
1004
    gnxrf_netf = ['svyr*']
1005
    gnxrf_bcgvbaf = ['ab-erphefr']
1006
  
1007
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1008
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1009
        vs vf_dhvrg():
1010
            ercbegre = nqq_ercbegre_ahyy
1011
        ryfr:
1012
            ercbegre = nqq_ercbegre_cevag
1013
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
1014
1015
1016
pynff pzq_zxqve(Pbzznaq):
1017
'''.splitlines(True), '''\
1018
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1019
1020
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
1021
    nqq gurz.
1022
    """
1023
    gnxrf_netf = ['svyr*']
1024
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
1025
1026
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
1027
        vzcbeg omeyvo.nqq
1028
1029
        vs qel_eha:
1030
            vs vf_dhvrg():
1031
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
1032
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
1033
            ryfr:
1034
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
1035
        ryvs vf_dhvrg():
1036
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
1037
        ryfr:
1038
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1039
1040
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1041
1042
1043
pynff pzq_zxqve(Pbzznaq):
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
1044
'''.splitlines(True)
1045
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1046
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1047
    def test_patience_unified_diff(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1048
        txt_a = ['hello there\n',
1049
                 'world\n',
1050
                 'how are you today?\n']
1051
        txt_b = ['hello there\n',
1052
                 'how are you today?\n']
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1053
        unified_diff = bzrlib.patiencediff.unified_diff
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1054
        psm = self._PatienceSequenceMatcher
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1055
        self.assertEquals([ '---  \n',
1056
                           '+++  \n',
1057
                           '@@ -1,3 +1,2 @@\n',
1058
                           ' hello there\n',
1059
                           '-world\n',
1060
                           ' how are you today?\n'
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1061
                          ]
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1062
                          , list(unified_diff(txt_a, txt_b,
1063
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1064
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1065
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1066
        # This is the result with LongestCommonSubstring matching
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1067
        self.assertEquals(['---  \n',
1068
                           '+++  \n',
1069
                           '@@ -1,6 +1,11 @@\n',
1070
                           ' a\n',
1071
                           ' b\n',
1072
                           ' c\n',
1073
                           '+d\n',
1074
                           '+e\n',
1075
                           '+f\n',
1076
                           '+x\n',
1077
                           '+y\n',
1078
                           ' d\n',
1079
                           ' e\n',
1080
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1081
                          , list(unified_diff(txt_a, txt_b)))
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1082
        # And the patience diff
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1083
        self.assertEquals(['---  \n',
1084
                           '+++  \n',
1085
                           '@@ -4,6 +4,11 @@\n',
1086
                           ' d\n',
1087
                           ' e\n',
1088
                           ' f\n',
1089
                           '+x\n',
1090
                           '+y\n',
1091
                           '+d\n',
1092
                           '+e\n',
1093
                           '+f\n',
1094
                           ' g\n',
1095
                           ' h\n',
1096
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1097
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1098
                          , list(unified_diff(txt_a, txt_b,
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1099
                                 sequencematcher=psm)))
1185.81.25 by Aaron Bentley
Clean up test_diff
1100
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1101
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1102
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1103
1104
    _test_needs_features = [CompiledPatienceDiffFeature]
1105
1106
    def setUp(self):
1107
        super(TestPatienceDiffLib_c, self).setUp()
1108
        import bzrlib._patiencediff_c
1109
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1110
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
1111
        self._PatienceSequenceMatcher = \
1112
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1113
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1114
    def test_unhashable(self):
1115
        """We should get a proper exception here."""
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1116
        # We need to be able to hash items in the sequence, lists are
1117
        # unhashable, and thus cannot be diffed
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1118
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1119
                                         None, [[]], [])
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1120
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1121
                                         None, ['valid', []], [])
1122
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1123
                                         None, ['valid'], [[]])
1124
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1125
                                         None, ['valid'], ['valid', []])
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1126
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1127
1711.2.15 by John Arbash Meinel
Found a couple CDV left
1128
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1129
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1130
    def setUp(self):
1131
        super(TestPatienceDiffLibFiles, self).setUp()
1132
        self._PatienceSequenceMatcher = \
1133
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
1134
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1135
    def test_patience_unified_diff_files(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1136
        txt_a = ['hello there\n',
1137
                 'world\n',
1138
                 'how are you today?\n']
1139
        txt_b = ['hello there\n',
1140
                 'how are you today?\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1141
        open('a1', 'wb').writelines(txt_a)
1142
        open('b1', 'wb').writelines(txt_b)
1143
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1144
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1145
        psm = self._PatienceSequenceMatcher
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1146
        self.assertEquals(['--- a1 \n',
1147
                           '+++ b1 \n',
1148
                           '@@ -1,3 +1,2 @@\n',
1149
                           ' hello there\n',
1150
                           '-world\n',
1151
                           ' how are you today?\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1152
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1153
                          , list(unified_diff_files('a1', 'b1',
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1154
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1155
1156
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1157
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1158
        open('a2', 'wb').writelines(txt_a)
1159
        open('b2', 'wb').writelines(txt_b)
1160
1161
        # This is the result with LongestCommonSubstring matching
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1162
        self.assertEquals(['--- a2 \n',
1163
                           '+++ b2 \n',
1164
                           '@@ -1,6 +1,11 @@\n',
1165
                           ' a\n',
1166
                           ' b\n',
1167
                           ' c\n',
1168
                           '+d\n',
1169
                           '+e\n',
1170
                           '+f\n',
1171
                           '+x\n',
1172
                           '+y\n',
1173
                           ' d\n',
1174
                           ' e\n',
1175
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1176
                          , list(unified_diff_files('a2', 'b2')))
1177
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1178
        # And the patience diff
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1179
        self.assertEquals(['--- a2 \n',
1180
                           '+++ b2 \n',
1181
                           '@@ -4,6 +4,11 @@\n',
1182
                           ' d\n',
1183
                           ' e\n',
1184
                           ' f\n',
1185
                           '+x\n',
1186
                           '+y\n',
1187
                           '+d\n',
1188
                           '+e\n',
1189
                           '+f\n',
1190
                           ' g\n',
1191
                           ' h\n',
1192
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1193
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1194
                          , list(unified_diff_files('a2', 'b2',
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1195
                                 sequencematcher=psm)))
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1196
1197
1198
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1199
1200
    _test_needs_features = [CompiledPatienceDiffFeature]
1201
1202
    def setUp(self):
1203
        super(TestPatienceDiffLibFiles_c, self).setUp()
1204
        import bzrlib._patiencediff_c
1205
        self._PatienceSequenceMatcher = \
1206
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1207
1208
1209
class TestUsingCompiledIfAvailable(TestCase):
1210
1211
    def test_PatienceSequenceMatcher(self):
1212
        if CompiledPatienceDiffFeature.available():
1213
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1214
            self.assertIs(PatienceSequenceMatcher_c,
1215
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1216
        else:
1217
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1218
            self.assertIs(PatienceSequenceMatcher_py,
1219
                          bzrlib.patiencediff.PatienceSequenceMatcher)
1220
1221
    def test_unique_lcs(self):
1222
        if CompiledPatienceDiffFeature.available():
1223
            from bzrlib._patiencediff_c import unique_lcs_c
1224
            self.assertIs(unique_lcs_c,
1225
                          bzrlib.patiencediff.unique_lcs)
1226
        else:
1227
            from bzrlib._patiencediff_py import unique_lcs_py
1228
            self.assertIs(unique_lcs_py,
1229
                          bzrlib.patiencediff.unique_lcs)
1230
1231
    def test_recurse_matches(self):
1232
        if CompiledPatienceDiffFeature.available():
1233
            from bzrlib._patiencediff_c import recurse_matches_c
1234
            self.assertIs(recurse_matches_c,
1235
                          bzrlib.patiencediff.recurse_matches)
1236
        else:
1237
            from bzrlib._patiencediff_py import recurse_matches_py
1238
            self.assertIs(recurse_matches_py,
1239
                          bzrlib.patiencediff.recurse_matches)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1240
1241
1242
class TestDiffFromTool(TestCaseWithTransport):
1243
1244
    def test_from_string(self):
1245
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
1246
        self.addCleanup(diff_obj.finish)
1247
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
1248
            diff_obj.command_template)
1249
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
1250
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
1251
                         diff_obj.command_template)
1252
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1253
                         diff_obj._get_command('old-path', 'new-path'))
1254
1255
    def test_execute(self):
1256
        output = StringIO()
1257
        diff_obj = DiffFromTool(['python', '-c',
1258
                                 'print "%(old_path)s %(new_path)s"'],
1259
                                None, None, output)
1260
        self.addCleanup(diff_obj.finish)
1261
        diff_obj._execute('old', 'new')
3146.4.2 by Aaron Bentley
Avoid assuming unix newline on output
1262
        self.assertEqual(output.getvalue().rstrip(), 'old new')
3123.6.2 by Aaron Bentley
Implement diff --using natively
1263
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
1264
    def test_excute_missing(self):
1265
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1266
                                None, None, None)
1267
        self.addCleanup(diff_obj.finish)
1268
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1269
                              'new')
1270
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1271
                         ' on this machine', str(e))
1272
3123.6.2 by Aaron Bentley
Implement diff --using natively
1273
    def test_prepare_files(self):
1274
        output = StringIO()
1275
        tree = self.make_branch_and_tree('tree')
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1276
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1277
        tree.add('oldname', 'file-id')
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
1278
        tree.commit('old tree', timestamp=0)
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1279
        tree.rename_one('oldname', 'newname')
1280
        self.build_tree_contents([('tree/newname', 'newcontent')])
3123.6.2 by Aaron Bentley
Implement diff --using natively
1281
        old_tree = tree.basis_tree()
1282
        old_tree.lock_read()
1283
        self.addCleanup(old_tree.unlock)
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
1284
        tree.lock_read()
1285
        self.addCleanup(tree.unlock)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1286
        diff_obj = DiffFromTool(['python', '-c',
1287
                                 'print "%(old_path)s %(new_path)s"'],
1288
                                old_tree, tree, output)
1289
        self.addCleanup(diff_obj.finish)
1290
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1291
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1292
                                                     'newname')
1293
        self.assertContainsRe(old_path, 'old/oldname$')
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
1294
        self.assertEqual(0, os.stat(old_path).st_mtime)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1295
        self.assertContainsRe(new_path, 'new/newname$')
1296
        self.assertFileEqual('oldcontent', old_path)
1297
        self.assertFileEqual('newcontent', new_path)
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1298
        if osutils.has_symlinks():
1299
            self.assertTrue(os.path.samefile('tree/newname', new_path))
3123.6.2 by Aaron Bentley
Implement diff --using natively
1300
        # make sure we can create files with the same parent directories
1301
        diff_obj._prepare_files('file-id', 'oldname2', 'newname2')