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