~bzr-pqm/bzr/bzr.dev

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
1692.8.5 by James Henstridge
merge from bzr.dev
23
from bzrlib.diff import internal_diff, external_diff, show_diff_trees
1711.2.56 by John Arbash Meinel
Raise NoDiff if 'diff' not present.
24
from bzrlib.errors import BinaryFile, NoDiff
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
25
import bzrlib.patiencediff
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
26
from bzrlib.tests import (TestCase, TestCaseWithTransport,
27
                          TestCaseInTempDir, TestSkipped)
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
28
29
1558.15.11 by Aaron Bentley
Apply merge review suggestions
30
def udiff_lines(old, new, allow_binary=False):
974.1.6 by Aaron Bentley
Added unit tests
31
    output = StringIO()
1558.15.11 by Aaron Bentley
Apply merge review suggestions
32
    internal_diff('old', old, 'new', new, output, allow_binary)
974.1.6 by Aaron Bentley
Added unit tests
33
    output.seek(0, 0)
34
    return output.readlines()
35
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
36
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
37
def external_udiff_lines(old, new, use_stringio=False):
38
    if use_stringio:
39
        # StringIO has no fileno, so it tests a different codepath
40
        output = StringIO()
41
    else:
42
        output = TemporaryFile()
1692.8.7 by James Henstridge
changes suggested by John Meinel
43
    try:
44
        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
45
    except NoDiff:
1711.2.56 by John Arbash Meinel
Raise NoDiff if 'diff' not present.
46
        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
47
    output.seek(0, 0)
48
    lines = output.readlines()
49
    output.close()
50
    return lines
51
52
1102 by Martin Pool
- merge test refactoring from robertc
53
class TestDiff(TestCase):
1185.81.25 by Aaron Bentley
Clean up test_diff
54
1102 by Martin Pool
- merge test refactoring from robertc
55
    def test_add_nl(self):
56
        """diff generates a valid diff for patches that add a newline"""
974.1.6 by Aaron Bentley
Added unit tests
57
        lines = udiff_lines(['boo'], ['boo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
58
        self.check_patch(lines)
59
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
60
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
61
1102 by Martin Pool
- merge test refactoring from robertc
62
    def test_add_nl_2(self):
63
        """diff generates a valid diff for patches that change last line and
64
        add a newline.
65
        """
974.1.6 by Aaron Bentley
Added unit tests
66
        lines = udiff_lines(['boo'], ['goo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
67
        self.check_patch(lines)
68
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
69
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
70
1102 by Martin Pool
- merge test refactoring from robertc
71
    def test_remove_nl(self):
72
        """diff generates a valid diff for patches that change last line and
73
        add a newline.
74
        """
974.1.6 by Aaron Bentley
Added unit tests
75
        lines = udiff_lines(['boo\n'], ['boo'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
76
        self.check_patch(lines)
77
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
78
            ## "expected no-nl, got %r" % lines[5]
79
80
    def check_patch(self, lines):
81
        self.assert_(len(lines) > 1)
82
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
83
        self.assert_(lines[0].startswith ('---'))
84
            ## 'No orig line for patch:\n%s' % "".join(lines)
85
        self.assert_(lines[1].startswith ('+++'))
86
            ## 'No mod line for patch:\n%s' % "".join(lines)
87
        self.assert_(len(lines) > 2)
88
            ## "No hunks for patch:\n%s" % "".join(lines)
89
        self.assert_(lines[2].startswith('@@'))
90
            ## "No hunk header for patch:\n%s" % "".join(lines)
91
        self.assert_('@@' in lines[2][2:])
92
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
93
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
94
    def test_binary_lines(self):
95
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
96
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
1558.15.11 by Aaron Bentley
Apply merge review suggestions
97
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
98
        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
99
100
    def test_external_diff(self):
101
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
102
        self.check_patch(lines)
1899.1.6 by John Arbash Meinel
internal_diff always adds a trailing \n, make sure external_diff does too
103
        self.assertEqual('\n', lines[-1])
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
104
105
    def test_external_diff_no_fileno(self):
106
        # Make sure that we can handle not having a fileno, even
107
        # if the diff is large
108
        lines = external_udiff_lines(['boo\n']*10000,
109
                                     ['goo\n']*10000,
110
                                     use_stringio=True)
111
        self.check_patch(lines)
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
112
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
113
    def test_external_diff_binary_lang_c(self):
114
        orig_lang = os.environ.get('LANG')
2138.1.1 by Wouter van Heyst
Robuster external diff output handling.
115
        orig_lc_all = os.environ.get('LC_ALL')
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
116
        try:
117
            os.environ['LANG'] = 'C'
2138.1.1 by Wouter van Heyst
Robuster external diff output handling.
118
            os.environ['LC_ALL'] = 'C'
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
119
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
1959.1.1 by Marien Zwart
merge.
120
            # Older versions of diffutils say "Binary files", newer
121
            # versions just say "Files".
122
            self.assertContainsRe(lines[0],
123
                                  '(Binary f|F)iles old and new differ\n')
124
            self.assertEquals(lines[1:], ['\n'])
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
125
        finally:
2138.1.1 by Wouter van Heyst
Robuster external diff output handling.
126
            for name, value in [('LANG', orig_lang), ('LC_ALL', orig_lc_all)]:
127
                if value is None:
128
                    del os.environ[name]
129
                else:
130
                    os.environ[name] = value
1899.1.4 by John Arbash Meinel
Just swallow a return code of 2
131
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
132
    def test_no_external_diff(self):
133
        """Check that NoDiff is raised when diff is not available"""
134
        # Use os.environ['PATH'] to make sure no 'diff' command is available
135
        orig_path = os.environ['PATH']
136
        try:
137
            os.environ['PATH'] = ''
138
            self.assertRaises(NoDiff, external_diff,
139
                              'old', ['boo\n'], 'new', ['goo\n'],
140
                              StringIO(), diff_opts=['-u'])
141
        finally:
142
            os.environ['PATH'] = orig_path
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
143
        
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
144
    def test_internal_diff_default(self):
145
        # Default internal diff encoding is utf8
146
        output = StringIO()
147
        internal_diff(u'old_\xb5', ['old_text\n'],
148
                    u'new_\xe5', ['new_text\n'], output)
149
        lines = output.getvalue().splitlines(True)
150
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
151
        self.assertEquals(['--- old_\xc2\xb5\n',
152
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
153
                           '@@ -1,1 +1,1 @@\n',
154
                           '-old_text\n',
155
                           '+new_text\n',
156
                           '\n',
157
                          ]
158
                          , lines)
159
160
    def test_internal_diff_utf8(self):
161
        output = StringIO()
162
        internal_diff(u'old_\xb5', ['old_text\n'],
163
                    u'new_\xe5', ['new_text\n'], output,
164
                    path_encoding='utf8')
165
        lines = output.getvalue().splitlines(True)
166
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
167
        self.assertEquals(['--- old_\xc2\xb5\n',
168
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
169
                           '@@ -1,1 +1,1 @@\n',
170
                           '-old_text\n',
171
                           '+new_text\n',
172
                           '\n',
173
                          ]
174
                          , lines)
175
176
    def test_internal_diff_iso_8859_1(self):
177
        output = StringIO()
178
        internal_diff(u'old_\xb5', ['old_text\n'],
179
                    u'new_\xe5', ['new_text\n'], output,
180
                    path_encoding='iso-8859-1')
181
        lines = output.getvalue().splitlines(True)
182
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
183
        self.assertEquals(['--- old_\xb5\n',
184
                           '+++ new_\xe5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
185
                           '@@ -1,1 +1,1 @@\n',
186
                           '-old_text\n',
187
                           '+new_text\n',
188
                           '\n',
189
                          ]
190
                          , lines)
191
192
    def test_internal_diff_returns_bytes(self):
193
        import StringIO
194
        output = StringIO.StringIO()
195
        internal_diff(u'old_\xb5', ['old_text\n'],
196
                    u'new_\xe5', ['new_text\n'], output)
197
        self.failUnless(isinstance(output.getvalue(), str),
198
            'internal_diff should return bytestrings')
199
1185.81.25 by Aaron Bentley
Clean up test_diff
200
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
201
class TestDiffFiles(TestCaseInTempDir):
202
203
    def test_external_diff_binary(self):
204
        """The output when using external diff should use diff's i18n error"""
205
        # Make sure external_diff doesn't fail in the current LANG
206
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
207
208
        cmd = ['diff', '-u', 'old', 'new']
209
        open('old', 'wb').write('\x00foobar\n')
210
        open('new', 'wb').write('foo\x00bar\n')
211
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
212
                                     stdin=subprocess.PIPE)
213
        out, err = pipe.communicate()
214
        # Diff returns '2' on Binary files.
215
        self.assertEqual(2, pipe.returncode)
216
        # We should output whatever diff tells us, plus a trailing newline
217
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
218
219
1740.2.5 by Aaron Bentley
Merge from bzr.dev
220
class TestDiffDates(TestCaseWithTransport):
221
222
    def setUp(self):
223
        super(TestDiffDates, self).setUp()
224
        self.wt = self.make_branch_and_tree('.')
225
        self.b = self.wt.branch
226
        self.build_tree_contents([
227
            ('file1', 'file1 contents at rev 1\n'),
228
            ('file2', 'file2 contents at rev 1\n')
229
            ])
230
        self.wt.add(['file1', 'file2'])
231
        self.wt.commit(
232
            message='Revision 1',
233
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
234
            timezone=0,
235
            rev_id='rev-1')
236
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
237
        self.wt.commit(
238
            message='Revision 2',
239
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
240
            timezone=28800,
241
            rev_id='rev-2')
242
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
243
        self.wt.commit(
244
            message='Revision 3',
245
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
246
            timezone=-3600,
247
            rev_id='rev-3')
248
        self.wt.remove(['file2'])
249
        self.wt.commit(
250
            message='Revision 4',
251
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
252
            timezone=0,
253
            rev_id='rev-4')
254
        self.build_tree_contents([
255
            ('file1', 'file1 contents in working tree\n')
256
            ])
257
        # set the date stamps for files in the working tree to known values
258
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
259
1551.7.22 by Aaron Bentley
Changes from review
260
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
1740.2.5 by Aaron Bentley
Merge from bzr.dev
261
        output = StringIO()
1551.7.22 by Aaron Bentley
Changes from review
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/')
1740.2.5 by Aaron Bentley
Merge from bzr.dev
269
        return output.getvalue()
270
271
    def test_diff_rev_tree_working_tree(self):
272
        output = self.get_diff(self.wt.basis_tree(), self.wt)
273
        # note that the date for old/file1 is from rev 2 rather than from
274
        # the basis revision (rev 4)
275
        self.assertEqualDiff(output, '''\
276
=== modified file 'file1'
277
--- old/file1\t2006-04-02 00:00:00 +0000
278
+++ new/file1\t2006-04-05 00:00:00 +0000
279
@@ -1,1 +1,1 @@
280
-file1 contents at rev 2
281
+file1 contents in working tree
282
283
''')
284
285
    def test_diff_rev_tree_rev_tree(self):
286
        tree1 = self.b.repository.revision_tree('rev-2')
287
        tree2 = self.b.repository.revision_tree('rev-3')
288
        output = self.get_diff(tree1, tree2)
289
        self.assertEqualDiff(output, '''\
290
=== modified file 'file2'
291
--- old/file2\t2006-04-01 00:00:00 +0000
292
+++ new/file2\t2006-04-03 00:00:00 +0000
293
@@ -1,1 +1,1 @@
294
-file2 contents at rev 1
295
+file2 contents at rev 3
296
297
''')
298
        
299
    def test_diff_add_files(self):
300
        tree1 = self.b.repository.revision_tree(None)
301
        tree2 = self.b.repository.revision_tree('rev-1')
302
        output = self.get_diff(tree1, tree2)
303
        # the files have the epoch time stamp for the tree in which
304
        # they don't exist.
305
        self.assertEqualDiff(output, '''\
306
=== added file 'file1'
307
--- old/file1\t1970-01-01 00:00:00 +0000
308
+++ new/file1\t2006-04-01 00:00:00 +0000
309
@@ -0,0 +1,1 @@
310
+file1 contents at rev 1
311
312
=== added file 'file2'
313
--- old/file2\t1970-01-01 00:00:00 +0000
314
+++ new/file2\t2006-04-01 00:00:00 +0000
315
@@ -0,0 +1,1 @@
316
+file2 contents at rev 1
317
318
''')
319
320
    def test_diff_remove_files(self):
321
        tree1 = self.b.repository.revision_tree('rev-3')
322
        tree2 = self.b.repository.revision_tree('rev-4')
323
        output = self.get_diff(tree1, tree2)
324
        # the file has the epoch time stamp for the tree in which
325
        # it doesn't exist.
326
        self.assertEqualDiff(output, '''\
327
=== removed file 'file2'
328
--- old/file2\t2006-04-03 00:00:00 +0000
329
+++ new/file2\t1970-01-01 00:00:00 +0000
330
@@ -1,1 +0,0 @@
331
-file2 contents at rev 3
332
333
''')
334
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
335
    def test_show_diff_specified(self):
1551.7.22 by Aaron Bentley
Changes from review
336
        """A working tree filename can be used to identify a file"""
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
337
        self.wt.rename_one('file1', 'file1b')
338
        old_tree = self.b.repository.revision_tree('rev-1')
339
        new_tree = self.b.repository.revision_tree('rev-4')
1551.7.22 by Aaron Bentley
Changes from review
340
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
341
                            working_tree=self.wt)
342
        self.assertContainsRe(out, 'file1\t')
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
343
1551.7.22 by Aaron Bentley
Changes from review
344
    def test_recursive_diff(self):
345
        """Children of directories are matched"""
346
        os.mkdir('dir1')
347
        os.mkdir('dir2')
348
        self.wt.add(['dir1', 'dir2'])
349
        self.wt.rename_one('file1', 'dir1/file1')
350
        old_tree = self.b.repository.revision_tree('rev-1')
351
        new_tree = self.b.repository.revision_tree('rev-4')
352
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
353
                            working_tree=self.wt)
354
        self.assertContainsRe(out, 'file1\t')
355
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
356
                            working_tree=self.wt)
357
        self.assertNotContainsRe(out, 'file1\t')
1740.2.5 by Aaron Bentley
Merge from bzr.dev
358
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
359
1711.2.15 by John Arbash Meinel
Found a couple CDV left
360
class TestPatienceDiffLib(TestCase):
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
361
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
362
    def test_unique_lcs(self):
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
363
        unique_lcs = bzrlib.patiencediff.unique_lcs
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
364
        self.assertEquals(unique_lcs('', ''), [])
365
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
366
        self.assertEquals(unique_lcs('a', 'b'), [])
367
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
368
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
369
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
370
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
371
                                                         (3,3), (4,4)])
372
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
373
374
    def test_recurse_matches(self):
375
        def test_one(a, b, matches):
376
            test_matches = []
1711.2.22 by John Arbash Meinel
Passing the alo parameter to recurse_matches shaves of 5% of the diff time.
377
            bzrlib.patiencediff.recurse_matches(a, b, 0, 0, len(a), len(b),
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
378
                test_matches, 10)
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
379
            self.assertEquals(test_matches, matches)
380
1711.2.17 by John Arbash Meinel
Small cleanups to patience_diff code.
381
        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,
382
                 [(0, 0), (2, 2), (4, 4)])
383
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
384
                 [(0, 0), (2, 1), (4, 2)])
385
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
386
        # recurse_matches doesn't match non-unique 
387
        # lines surrounded by bogus text.
1185.81.24 by Aaron Bentley
Reoganize patience-related code
388
        # 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
389
390
        # This is what it could be
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
391
        #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
392
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
393
        # This is what it currently gives:
394
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
395
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
396
    def test_matching_blocks(self):
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
397
        def chk_blocks(a, b, expected_blocks):
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
398
            # difflib always adds a signature of the total
399
            # length, with no matching entries at the end
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
400
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
1185.81.11 by John Arbash Meinel
Found some edge cases that weren't being matched.
401
            blocks = s.get_matching_blocks()
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
402
            self.assertEquals((len(a), len(b), 0), blocks[-1])
403
            self.assertEquals(expected_blocks, blocks[:-1])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
404
1185.81.2 by John Arbash Meinel
A couple small tests.
405
        # Some basic matching tests
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
406
        chk_blocks('', '', [])
407
        chk_blocks([], [], [])
408
        chk_blocks('abcd', 'abcd', [(0, 0, 4)])
409
        chk_blocks('abcd', 'abce', [(0, 0, 3)])
410
        chk_blocks('eabc', 'abce', [(1, 0, 3)])
411
        chk_blocks('eabce', 'abce', [(1, 0, 4)])
412
        chk_blocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
413
        chk_blocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
414
        chk_blocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
415
        # This may check too much, but it checks to see that 
416
        # a copied block stays attached to the previous section,
417
        # not the later one.
418
        # difflib would tend to grab the trailing longest match
419
        # which would make the diff not look right
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
420
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
421
                   [(0, 0, 6), (6, 11, 10)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
422
1185.81.2 by John Arbash Meinel
A couple small tests.
423
        # make sure it supports passing in lists
424
        chk_blocks(
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
425
                   ['hello there\n',
426
                    'world\n',
427
                    'how are you today?\n'],
428
                   ['hello there\n',
429
                    'how are you today?\n'],
1185.81.2 by John Arbash Meinel
A couple small tests.
430
                [(0, 0, 1), (2, 1, 1)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
431
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
432
        # non unique lines surrounded by non-matching lines
433
        # won't be found
434
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
435
436
        # But they only need to be locally unique
437
        chk_blocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
438
439
        # non unique blocks won't be matched
440
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
441
442
        # but locally unique ones will
443
        chk_blocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
444
                                              (5,4,1), (7,5,2), (10,8,1)])
445
446
        chk_blocks('abbabbXd', 'cabbabxd', [(7,7,1)])
447
        chk_blocks('abbabbbb', 'cabbabbc', [])
448
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [])
1185.81.11 by John Arbash Meinel
Found some edge cases that weren't being matched.
449
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
450
    def test_opcodes(self):
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
451
        def chk_ops(a, b, expected_codes):
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
452
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
453
            self.assertEquals(expected_codes, s.get_opcodes())
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
454
455
        chk_ops('', '', [])
456
        chk_ops([], [], [])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
457
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
458
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
459
                                 ('replace', 3,4, 3,4)
460
                                ])
461
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
462
                                 ('equal',  1,4, 0,3),
463
                                 ('insert', 4,4, 3,4)
464
                                ])
465
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
466
                                  ('equal',  1,5, 0,4)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
467
                                 ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
468
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
469
                                   ('replace', 2,3, 2,3),
470
                                   ('equal',   3,5, 3,5)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
471
                                  ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
472
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
473
                                     ('replace', 2,3, 2,5),
474
                                     ('equal',   3,5, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
475
                                    ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
476
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
477
                                    ('insert', 2,2, 2,5),
478
                                    ('equal',  2,4, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
479
                                   ])
480
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
481
                [('equal',  0,6,  0,6),
482
                 ('insert', 6,6,  6,11),
483
                 ('equal',  6,16, 11,21)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
484
                ])
485
        chk_ops(
486
                [ 'hello there\n'
487
                , 'world\n'
488
                , 'how are you today?\n'],
489
                [ 'hello there\n'
490
                , 'how are you today?\n'],
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
491
                [('equal',  0,1, 0,1),
492
                 ('delete', 1,2, 1,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
493
                 ('equal',  2,3, 1,2),
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
494
                ])
495
        chk_ops('aBccDe', 'abccde', 
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
496
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
497
                 ('replace', 1,5, 1,5),
498
                 ('equal',   5,6, 5,6),
499
                ])
500
        chk_ops('aBcDec', 'abcdec', 
501
                [('equal',   0,1, 0,1),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
502
                 ('replace', 1,2, 1,2),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
503
                 ('equal',   2,3, 2,3),
504
                 ('replace', 3,4, 3,4),
505
                 ('equal',   4,6, 4,6),
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
506
                ])
1185.81.10 by John Arbash Meinel
Added some more test cases.
507
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
508
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
509
                 ('replace', 1,8, 1,8),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
510
                 ('equal',   8,9, 8,9)
1185.81.10 by John Arbash Meinel
Added some more test cases.
511
                ])
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
512
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
513
                [('equal',   0,1, 0,1),
514
                 ('replace', 1,2, 1,2),
515
                 ('equal',   2,4, 2,4),
516
                 ('delete', 4,5, 4,4),
517
                 ('equal',   5,6, 4,5),
518
                 ('delete', 6,7, 5,5),
519
                 ('equal',   7,9, 5,7),
520
                 ('replace', 9,10, 7,8),
521
                 ('equal',   10,11, 8,9)
522
                ])
1185.81.10 by John Arbash Meinel
Added some more test cases.
523
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
524
    def test_multiple_ranges(self):
525
        # There was an earlier bug where we used a bad set of ranges,
526
        # this triggers that specific bug, to make sure it doesn't regress
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
527
        def chk_blocks(a, b, expected_blocks):
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
528
            # difflib always adds a signature of the total
529
            # length, with no matching entries at the end
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
530
            s = bzrlib.patiencediff.PatienceSequenceMatcher(None, a, b)
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
531
            blocks = s.get_matching_blocks()
532
            x = blocks.pop()
533
            self.assertEquals(x, (len(a), len(b), 0))
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
534
            self.assertEquals(expected_blocks, blocks)
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
535
536
        chk_blocks('abcdefghijklmnop'
537
                 , 'abcXghiYZQRSTUVWXYZijklmnop'
538
                 , [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
539
540
        chk_blocks('ABCd efghIjk  L'
541
                 , 'AxyzBCn mo pqrstuvwI1 2  L'
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
542
                 , [(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.
543
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
544
        # These are rot13 code snippets.
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
545
        chk_blocks('''\
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
546
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
547
    """
548
    gnxrf_netf = ['svyr*']
549
    gnxrf_bcgvbaf = ['ab-erphefr']
550
  
551
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
552
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
553
        vs vf_dhvrg():
554
            ercbegre = nqq_ercbegre_ahyy
555
        ryfr:
556
            ercbegre = nqq_ercbegre_cevag
557
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
558
559
560
pynff pzq_zxqve(Pbzznaq):
561
'''.splitlines(True), '''\
562
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
563
564
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
565
    nqq gurz.
566
    """
567
    gnxrf_netf = ['svyr*']
568
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
569
570
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
571
        vzcbeg omeyvo.nqq
572
573
        vs qel_eha:
574
            vs vf_dhvrg():
575
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
576
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
577
            ryfr:
578
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
579
        ryvs vf_dhvrg():
580
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
581
        ryfr:
582
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
583
584
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
585
586
587
pynff pzq_zxqve(Pbzznaq):
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
588
'''.splitlines(True)
589
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
590
1711.2.9 by John Arbash Meinel
Rename cdv => patience
591
    def test_patience_unified_diff(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
592
        txt_a = ['hello there\n',
593
                 'world\n',
594
                 'how are you today?\n']
595
        txt_b = ['hello there\n',
596
                 'how are you today?\n']
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
597
        unified_diff = bzrlib.patiencediff.unified_diff
598
        psm = bzrlib.patiencediff.PatienceSequenceMatcher
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
599
        self.assertEquals([ '---  \n',
600
                           '+++  \n',
601
                           '@@ -1,3 +1,2 @@\n',
602
                           ' hello there\n',
603
                           '-world\n',
604
                           ' 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
605
                          ]
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
606
                          , list(unified_diff(txt_a, txt_b,
607
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
608
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
609
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
610
        # This is the result with LongestCommonSubstring matching
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
611
        self.assertEquals(['---  \n',
612
                           '+++  \n',
613
                           '@@ -1,6 +1,11 @@\n',
614
                           ' a\n',
615
                           ' b\n',
616
                           ' c\n',
617
                           '+d\n',
618
                           '+e\n',
619
                           '+f\n',
620
                           '+x\n',
621
                           '+y\n',
622
                           ' d\n',
623
                           ' e\n',
624
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
625
                          , list(unified_diff(txt_a, txt_b)))
1711.2.9 by John Arbash Meinel
Rename cdv => patience
626
        # And the patience diff
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
627
        self.assertEquals(['---  \n',
628
                           '+++  \n',
629
                           '@@ -4,6 +4,11 @@\n',
630
                           ' d\n',
631
                           ' e\n',
632
                           ' f\n',
633
                           '+x\n',
634
                           '+y\n',
635
                           '+d\n',
636
                           '+e\n',
637
                           '+f\n',
638
                           ' g\n',
639
                           ' h\n',
640
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
641
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
642
                          , 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
643
                                 sequencematcher=psm)))
1185.81.25 by Aaron Bentley
Clean up test_diff
644
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
645
1711.2.15 by John Arbash Meinel
Found a couple CDV left
646
class TestPatienceDiffLibFiles(TestCaseInTempDir):
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
647
1711.2.9 by John Arbash Meinel
Rename cdv => patience
648
    def test_patience_unified_diff_files(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
649
        txt_a = ['hello there\n',
650
                 'world\n',
651
                 'how are you today?\n']
652
        txt_b = ['hello there\n',
653
                 '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
654
        open('a1', 'wb').writelines(txt_a)
655
        open('b1', 'wb').writelines(txt_b)
656
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
657
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
658
        psm = bzrlib.patiencediff.PatienceSequenceMatcher
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
659
        self.assertEquals(['--- a1 \n',
660
                           '+++ b1 \n',
661
                           '@@ -1,3 +1,2 @@\n',
662
                           ' hello there\n',
663
                           '-world\n',
664
                           ' 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
665
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
666
                          , 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
667
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
668
669
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
670
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
671
        open('a2', 'wb').writelines(txt_a)
672
        open('b2', 'wb').writelines(txt_b)
673
674
        # This is the result with LongestCommonSubstring matching
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
675
        self.assertEquals(['--- a2 \n',
676
                           '+++ b2 \n',
677
                           '@@ -1,6 +1,11 @@\n',
678
                           ' a\n',
679
                           ' b\n',
680
                           ' c\n',
681
                           '+d\n',
682
                           '+e\n',
683
                           '+f\n',
684
                           '+x\n',
685
                           '+y\n',
686
                           ' d\n',
687
                           ' e\n',
688
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
689
                          , list(unified_diff_files('a2', 'b2')))
690
1711.2.9 by John Arbash Meinel
Rename cdv => patience
691
        # And the patience diff
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
692
        self.assertEquals(['--- a2 \n',
693
                           '+++ b2 \n',
694
                           '@@ -4,6 +4,11 @@\n',
695
                           ' d\n',
696
                           ' e\n',
697
                           ' f\n',
698
                           '+x\n',
699
                           '+y\n',
700
                           '+d\n',
701
                           '+e\n',
702
                           '+f\n',
703
                           ' g\n',
704
                           ' h\n',
705
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
706
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
707
                          , 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
708
                                 sequencematcher=psm)))