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