~bzr-pqm/bzr/bzr.dev

6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
1
# Copyright (C) 2005-2014 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1711.2.16 by John Arbash Meinel
test_diff needs a copyright statement
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
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
19
import subprocess
3287.18.6 by Matt McClure
Snapshot of unfinished work.
20
import sys
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
21
import tempfile
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
22
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
23
from bzrlib import (
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
24
    diff,
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
25
    errors,
26
    osutils,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
27
    patiencediff,
28
    _patiencediff_py,
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
29
    revision as _mod_revision,
30
    revisionspec,
31
    revisiontree,
32
    tests,
33
    transform,
34
    )
5147.3.7 by Andrew Bennetts
Expect DeprecationWarnings for get_trees_and_branches_to_diff in test_diff, and add corresponding test coverage for get_trees_and_branches_to_diff_locked.
35
from bzrlib.symbol_versioning import deprecated_in
5074.5.3 by INADA Naoki
merge lp:bzr
36
from bzrlib.tests import features, EncodingAdapter
4797.57.6 by Alexander Belchenko
added whitebox test for path_encoding in diff.
37
from bzrlib.tests.blackbox.test_diff import subst_dates
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
38
from bzrlib.tests import (
39
    features,
40
    )
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
41
42
1558.15.11 by Aaron Bentley
Apply merge review suggestions
43
def udiff_lines(old, new, allow_binary=False):
974.1.6 by Aaron Bentley
Added unit tests
44
    output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
45
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
974.1.6 by Aaron Bentley
Added unit tests
46
    output.seek(0, 0)
47
    return output.readlines()
48
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
49
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
50
def external_udiff_lines(old, new, use_stringio=False):
51
    if use_stringio:
52
        # StringIO has no fileno, so it tests a different codepath
53
        output = StringIO()
54
    else:
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
55
        output = tempfile.TemporaryFile()
1692.8.7 by James Henstridge
changes suggested by John Meinel
56
    try:
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
57
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
58
    except errors.NoDiff:
59
        raise tests.TestSkipped('external "diff" not present to test')
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
60
    output.seek(0, 0)
61
    lines = output.readlines()
62
    output.close()
63
    return lines
64
65
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
66
class TestDiff(tests.TestCase):
1185.81.25 by Aaron Bentley
Clean up test_diff
67
1102 by Martin Pool
- merge test refactoring from robertc
68
    def test_add_nl(self):
69
        """diff generates a valid diff for patches that add a newline"""
974.1.6 by Aaron Bentley
Added unit tests
70
        lines = udiff_lines(['boo'], ['boo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
71
        self.check_patch(lines)
72
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
73
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
74
1102 by Martin Pool
- merge test refactoring from robertc
75
    def test_add_nl_2(self):
76
        """diff generates a valid diff for patches that change last line and
77
        add a newline.
78
        """
974.1.6 by Aaron Bentley
Added unit tests
79
        lines = udiff_lines(['boo'], ['goo\n'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
80
        self.check_patch(lines)
81
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
82
            ## "expected no-nl, got %r" % lines[4]
974.1.6 by Aaron Bentley
Added unit tests
83
1102 by Martin Pool
- merge test refactoring from robertc
84
    def test_remove_nl(self):
85
        """diff generates a valid diff for patches that change last line and
86
        add a newline.
87
        """
974.1.6 by Aaron Bentley
Added unit tests
88
        lines = udiff_lines(['boo\n'], ['boo'])
1185.16.145 by Martin Pool
Remove all assert statements from test cases.
89
        self.check_patch(lines)
90
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
91
            ## "expected no-nl, got %r" % lines[5]
92
93
    def check_patch(self, lines):
94
        self.assert_(len(lines) > 1)
95
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
96
        self.assert_(lines[0].startswith ('---'))
97
            ## 'No orig line for patch:\n%s' % "".join(lines)
98
        self.assert_(lines[1].startswith ('+++'))
99
            ## 'No mod line for patch:\n%s' % "".join(lines)
100
        self.assert_(len(lines) > 2)
101
            ## "No hunks for patch:\n%s" % "".join(lines)
102
        self.assert_(lines[2].startswith('@@'))
103
            ## "No hunk header for patch:\n%s" % "".join(lines)
104
        self.assert_('@@' in lines[2][2:])
105
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
106
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
107
    def test_binary_lines(self):
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
108
        empty = []
109
        uni_lines = [1023 * 'a' + '\x00']
110
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
111
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
112
        udiff_lines(uni_lines , empty, allow_binary=True)
113
        udiff_lines(empty, uni_lines, allow_binary=True)
1692.8.2 by James Henstridge
add a test for sending external diff output to a file
114
115
    def test_external_diff(self):
116
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
117
        self.check_patch(lines)
1899.1.6 by John Arbash Meinel
internal_diff always adds a trailing \n, make sure external_diff does too
118
        self.assertEqual('\n', lines[-1])
1711.2.57 by John Arbash Meinel
Allow external diff to write to a file without a fileno.
119
120
    def test_external_diff_no_fileno(self):
121
        # Make sure that we can handle not having a fileno, even
122
        # if the diff is large
123
        lines = external_udiff_lines(['boo\n']*10000,
124
                                     ['goo\n']*10000,
125
                                     use_stringio=True)
126
        self.check_patch(lines)
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
127
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
128
    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)
129
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
5570.3.9 by Vincent Ladeuil
More use cases for overrideEnv, _cleanEnvironment *may* contain too much variables now.
130
            self.overrideEnv(lang, 'C')
131
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
132
        # Older versions of diffutils say "Binary files", newer
133
        # versions just say "Files".
134
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
135
        self.assertEquals(lines[1:], ['\n'])
1899.1.4 by John Arbash Meinel
Just swallow a return code of 2
136
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
137
    def test_no_external_diff(self):
138
        """Check that NoDiff is raised when diff is not available"""
5570.3.9 by Vincent Ladeuil
More use cases for overrideEnv, _cleanEnvironment *may* contain too much variables now.
139
        # Make sure no 'diff' command is available
140
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
141
        self.overrideEnv('PATH', '')
142
        self.assertRaises(errors.NoDiff, diff.external_diff,
143
                          'old', ['boo\n'], 'new', ['goo\n'],
144
                          StringIO(), diff_opts=['-u'])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
145
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
146
    def test_default_style_unified(self):
147
        """Check for default style '-u' only if no other style specified
148
        in 'diff-options'.
149
        """
150
        # Verify that style defaults to unified, id est '-u' appended
151
        # to option list, in the absence of an alternative style.
152
        self.assertEqual(['-a', '-u'], diff.default_style_unified(["-a"]))
153
        # Verify that for all valid style options, '-u' is not
154
        # appended to option list.
155
        for s in diff.style_option_list:
156
            ret_opts = diff.default_style_unified(diff_opts=["%s" % (s,)])
157
            self.assertEqual(["%s" % (s,)], ret_opts)
158
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
159
    def test_internal_diff_default(self):
160
        # Default internal diff encoding is utf8
161
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
162
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
163
                           u'new_\xe5', ['new_text\n'], output)
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
164
        lines = output.getvalue().splitlines(True)
165
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
166
        self.assertEquals(['--- old_\xc2\xb5\n',
167
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
168
                           '@@ -1,1 +1,1 @@\n',
169
                           '-old_text\n',
170
                           '+new_text\n',
171
                           '\n',
172
                          ]
173
                          , lines)
174
175
    def test_internal_diff_utf8(self):
176
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
177
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
178
                           u'new_\xe5', ['new_text\n'], output,
179
                           path_encoding='utf8')
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
180
        lines = output.getvalue().splitlines(True)
181
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
182
        self.assertEquals(['--- old_\xc2\xb5\n',
183
                           '+++ new_\xc3\xa5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
184
                           '@@ -1,1 +1,1 @@\n',
185
                           '-old_text\n',
186
                           '+new_text\n',
187
                           '\n',
188
                          ]
189
                          , lines)
190
191
    def test_internal_diff_iso_8859_1(self):
192
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
193
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
194
                           u'new_\xe5', ['new_text\n'], output,
195
                           path_encoding='iso-8859-1')
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
196
        lines = output.getvalue().splitlines(True)
197
        self.check_patch(lines)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
198
        self.assertEquals(['--- old_\xb5\n',
199
                           '+++ new_\xe5\n',
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
200
                           '@@ -1,1 +1,1 @@\n',
201
                           '-old_text\n',
202
                           '+new_text\n',
203
                           '\n',
204
                          ]
205
                          , lines)
206
3085.1.1 by John Arbash Meinel
Fix internal_diff to not fail when the texts are identical.
207
    def test_internal_diff_no_content(self):
208
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
209
        diff.internal_diff(u'old', [], u'new', [], output)
3085.1.1 by John Arbash Meinel
Fix internal_diff to not fail when the texts are identical.
210
        self.assertEqual('', output.getvalue())
211
212
    def test_internal_diff_no_changes(self):
213
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
214
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
215
                           u'new', ['text\n', 'contents\n'],
216
                           output)
3085.1.1 by John Arbash Meinel
Fix internal_diff to not fail when the texts are identical.
217
        self.assertEqual('', output.getvalue())
218
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
219
    def test_internal_diff_returns_bytes(self):
220
        import StringIO
221
        output = StringIO.StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
222
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
223
                            u'new_\xe5', ['new_text\n'], output)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
224
        self.assertIsInstance(output.getvalue(), str,
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
225
            'internal_diff should return bytestrings')
226
6524.5.5 by Paul Nixon
Added tests of configurable context
227
    def test_internal_diff_default_context(self):
228
        output = StringIO()
229
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
230
                           'same_text\n','same_text\n','old_text\n'],
231
                           'new', ['same_text\n','same_text\n','same_text\n',
232
                           'same_text\n','same_text\n','new_text\n'], output)
233
        lines = output.getvalue().splitlines(True)
234
        self.check_patch(lines)
235
        self.assertEquals(['--- old\n',
236
                           '+++ new\n',
237
                           '@@ -3,4 +3,4 @@\n',
238
                           ' same_text\n',
239
                           ' same_text\n',
240
                           ' same_text\n',
241
                           '-old_text\n',
242
                           '+new_text\n',
243
                           '\n',
244
                          ]
245
                          , lines)
246
247
    def test_internal_diff_no_context(self):
248
        output = StringIO()
249
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
250
                           'same_text\n','same_text\n','old_text\n'],
251
                           'new', ['same_text\n','same_text\n','same_text\n',
252
                           'same_text\n','same_text\n','new_text\n'], output,
253
                           context_lines=0)
254
        lines = output.getvalue().splitlines(True)
255
        self.check_patch(lines)
256
        self.assertEquals(['--- old\n',
257
                           '+++ new\n',
258
                           '@@ -6,1 +6,1 @@\n',
259
                           '-old_text\n',
260
                           '+new_text\n',
261
                           '\n',
262
                          ]
263
                          , lines)
264
265
    def test_internal_diff_more_context(self):
266
        output = StringIO()
267
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
268
                           'same_text\n','same_text\n','old_text\n'],
269
                           'new', ['same_text\n','same_text\n','same_text\n',
270
                           'same_text\n','same_text\n','new_text\n'], output,
271
                           context_lines=4)
272
        lines = output.getvalue().splitlines(True)
273
        self.check_patch(lines)
274
        self.assertEquals(['--- old\n',
275
                           '+++ new\n',
276
                           '@@ -2,5 +2,5 @@\n',
277
                           ' same_text\n',
278
                           ' same_text\n',
279
                           ' same_text\n',
280
                           ' same_text\n',
281
                           '-old_text\n',
282
                           '+new_text\n',
283
                           '\n',
284
                          ]
285
                          , lines)
286
287
288
289
1185.81.25 by Aaron Bentley
Clean up test_diff
290
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
291
class TestDiffFiles(tests.TestCaseInTempDir):
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
292
293
    def test_external_diff_binary(self):
294
        """The output when using external diff should use diff's i18n error"""
295
        # Make sure external_diff doesn't fail in the current LANG
296
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
297
2240.1.1 by Alexander Belchenko
test_external_diff_binary: run external diff with --binary flag
298
        cmd = ['diff', '-u', '--binary', 'old', 'new']
6437.20.3 by Wouter van Heyst
mechanically replace file().write() pattern with a with-keyword version
299
        with open('old', 'wb') as f: f.write('\x00foobar\n')
300
        with open('new', 'wb') as f: f.write('foo\x00bar\n')
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
301
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
302
                                     stdin=subprocess.PIPE)
303
        out, err = pipe.communicate()
304
        # Diff returns '2' on Binary files.
305
        self.assertEqual(2, pipe.returncode)
306
        # We should output whatever diff tells us, plus a trailing newline
307
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
308
309
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
310
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
311
    output = StringIO()
312
    if working_tree is not None:
313
        extra_trees = (working_tree,)
314
    else:
315
        extra_trees = ()
316
    diff.show_diff_trees(tree1, tree2, output,
317
        specific_files=specific_files,
318
        extra_trees=extra_trees, old_label='old/',
319
        new_label='new/')
320
    return output.getvalue()
321
322
323
class TestDiffDates(tests.TestCaseWithTransport):
1740.2.5 by Aaron Bentley
Merge from bzr.dev
324
325
    def setUp(self):
326
        super(TestDiffDates, self).setUp()
327
        self.wt = self.make_branch_and_tree('.')
328
        self.b = self.wt.branch
329
        self.build_tree_contents([
330
            ('file1', 'file1 contents at rev 1\n'),
331
            ('file2', 'file2 contents at rev 1\n')
332
            ])
333
        self.wt.add(['file1', 'file2'])
334
        self.wt.commit(
335
            message='Revision 1',
336
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
337
            timezone=0,
338
            rev_id='rev-1')
339
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
340
        self.wt.commit(
341
            message='Revision 2',
342
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
343
            timezone=28800,
344
            rev_id='rev-2')
345
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
346
        self.wt.commit(
347
            message='Revision 3',
348
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
349
            timezone=-3600,
350
            rev_id='rev-3')
351
        self.wt.remove(['file2'])
352
        self.wt.commit(
353
            message='Revision 4',
354
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
355
            timezone=0,
356
            rev_id='rev-4')
357
        self.build_tree_contents([
358
            ('file1', 'file1 contents in working tree\n')
359
            ])
360
        # set the date stamps for files in the working tree to known values
361
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
362
363
    def test_diff_rev_tree_working_tree(self):
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
364
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
365
        # note that the date for old/file1 is from rev 2 rather than from
366
        # the basis revision (rev 4)
367
        self.assertEqualDiff(output, '''\
368
=== modified file 'file1'
369
--- old/file1\t2006-04-02 00:00:00 +0000
370
+++ new/file1\t2006-04-05 00:00:00 +0000
371
@@ -1,1 +1,1 @@
372
-file1 contents at rev 2
373
+file1 contents in working tree
374
375
''')
376
377
    def test_diff_rev_tree_rev_tree(self):
378
        tree1 = self.b.repository.revision_tree('rev-2')
379
        tree2 = self.b.repository.revision_tree('rev-3')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
380
        output = get_diff_as_string(tree1, tree2)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
381
        self.assertEqualDiff(output, '''\
382
=== modified file 'file2'
383
--- old/file2\t2006-04-01 00:00:00 +0000
384
+++ new/file2\t2006-04-03 00:00:00 +0000
385
@@ -1,1 +1,1 @@
386
-file2 contents at rev 1
387
+file2 contents at rev 3
388
389
''')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
390
1740.2.5 by Aaron Bentley
Merge from bzr.dev
391
    def test_diff_add_files(self):
3668.5.1 by Jelmer Vernooij
Use NULL_REVISION rather than None for Repository.revision_tree().
392
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
393
        tree2 = self.b.repository.revision_tree('rev-1')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
394
        output = get_diff_as_string(tree1, tree2)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
395
        # the files have the epoch time stamp for the tree in which
396
        # they don't exist.
397
        self.assertEqualDiff(output, '''\
398
=== added file 'file1'
399
--- old/file1\t1970-01-01 00:00:00 +0000
400
+++ new/file1\t2006-04-01 00:00:00 +0000
401
@@ -0,0 +1,1 @@
402
+file1 contents at rev 1
403
404
=== added file 'file2'
405
--- old/file2\t1970-01-01 00:00:00 +0000
406
+++ new/file2\t2006-04-01 00:00:00 +0000
407
@@ -0,0 +1,1 @@
408
+file2 contents at rev 1
409
410
''')
411
412
    def test_diff_remove_files(self):
413
        tree1 = self.b.repository.revision_tree('rev-3')
414
        tree2 = self.b.repository.revision_tree('rev-4')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
415
        output = get_diff_as_string(tree1, tree2)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
416
        # the file has the epoch time stamp for the tree in which
417
        # it doesn't exist.
418
        self.assertEqualDiff(output, '''\
419
=== removed file 'file2'
420
--- old/file2\t2006-04-03 00:00:00 +0000
421
+++ new/file2\t1970-01-01 00:00:00 +0000
422
@@ -1,1 +0,0 @@
423
-file2 contents at rev 3
424
425
''')
426
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
427
    def test_show_diff_specified(self):
1551.7.22 by Aaron Bentley
Changes from review
428
        """A working tree filename can be used to identify a file"""
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
429
        self.wt.rename_one('file1', 'file1b')
430
        old_tree = self.b.repository.revision_tree('rev-1')
431
        new_tree = self.b.repository.revision_tree('rev-4')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
432
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
1551.7.22 by Aaron Bentley
Changes from review
433
                            working_tree=self.wt)
434
        self.assertContainsRe(out, 'file1\t')
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
435
1551.7.22 by Aaron Bentley
Changes from review
436
    def test_recursive_diff(self):
437
        """Children of directories are matched"""
438
        os.mkdir('dir1')
439
        os.mkdir('dir2')
440
        self.wt.add(['dir1', 'dir2'])
441
        self.wt.rename_one('file1', 'dir1/file1')
442
        old_tree = self.b.repository.revision_tree('rev-1')
443
        new_tree = self.b.repository.revision_tree('rev-4')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
444
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
1551.7.22 by Aaron Bentley
Changes from review
445
                            working_tree=self.wt)
446
        self.assertContainsRe(out, 'file1\t')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
447
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
1551.7.22 by Aaron Bentley
Changes from review
448
                            working_tree=self.wt)
449
        self.assertNotContainsRe(out, 'file1\t')
1740.2.5 by Aaron Bentley
Merge from bzr.dev
450
1899.1.1 by John Arbash Meinel
Fix the bug in the NoDiff exception class, and add a test
451
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
452
class TestShowDiffTrees(tests.TestCaseWithTransport):
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
453
    """Direct tests for show_diff_trees"""
454
455
    def test_modified_file(self):
456
        """Test when a file is modified."""
457
        tree = self.make_branch_and_tree('tree')
458
        self.build_tree_contents([('tree/file', 'contents\n')])
459
        tree.add(['file'], ['file-id'])
460
        tree.commit('one', rev_id='rev-1')
461
462
        self.build_tree_contents([('tree/file', 'new contents\n')])
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
463
        d = get_diff_as_string(tree.basis_tree(), tree)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
464
        self.assertContainsRe(d, "=== modified file 'file'\n")
465
        self.assertContainsRe(d, '--- old/file\t')
466
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
467
        self.assertContainsRe(d, '-contents\n'
468
                                 '\\+new contents\n')
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
469
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
470
    def test_modified_file_in_renamed_dir(self):
471
        """Test when a file is modified in a renamed directory."""
472
        tree = self.make_branch_and_tree('tree')
473
        self.build_tree(['tree/dir/'])
474
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
475
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
476
        tree.commit('one', rev_id='rev-1')
477
478
        tree.rename_one('dir', 'other')
479
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
480
        d = get_diff_as_string(tree.basis_tree(), tree)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
481
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
482
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
483
        # XXX: This is technically incorrect, because it used to be at another
484
        # location. What to do?
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
485
        self.assertContainsRe(d, '--- old/dir/file\t')
486
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
487
        self.assertContainsRe(d, '-contents\n'
488
                                 '\\+new contents\n')
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
489
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
490
    def test_renamed_directory(self):
491
        """Test when only a directory is only renamed."""
492
        tree = self.make_branch_and_tree('tree')
493
        self.build_tree(['tree/dir/'])
494
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
495
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
496
        tree.commit('one', rev_id='rev-1')
497
498
        tree.rename_one('dir', 'newdir')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
499
        d = get_diff_as_string(tree.basis_tree(), tree)
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
500
        # Renaming a directory should be a single "you renamed this dir" even
501
        # when there are files inside.
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
502
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
503
504
    def test_renamed_file(self):
505
        """Test when a file is only renamed."""
506
        tree = self.make_branch_and_tree('tree')
507
        self.build_tree_contents([('tree/file', 'contents\n')])
508
        tree.add(['file'], ['file-id'])
509
        tree.commit('one', rev_id='rev-1')
510
511
        tree.rename_one('file', 'newname')
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
512
        d = get_diff_as_string(tree.basis_tree(), tree)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
513
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
514
        # We shouldn't have a --- or +++ line, because there is no content
515
        # change
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
516
        self.assertNotContainsRe(d, '---')
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
517
518
    def test_renamed_and_modified_file(self):
519
        """Test when a file is only renamed."""
520
        tree = self.make_branch_and_tree('tree')
521
        self.build_tree_contents([('tree/file', 'contents\n')])
522
        tree.add(['file'], ['file-id'])
523
        tree.commit('one', rev_id='rev-1')
524
525
        tree.rename_one('file', 'newname')
526
        self.build_tree_contents([('tree/newname', 'new contents\n')])
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
527
        d = get_diff_as_string(tree.basis_tree(), tree)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
528
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
529
        self.assertContainsRe(d, '--- old/file\t')
530
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
531
        self.assertContainsRe(d, '-contents\n'
532
                                 '\\+new contents\n')
2405.1.1 by John Arbash Meinel
Add a bunch of direct tests for 'show_diff_trees'
533
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
534
535
    def test_internal_diff_exec_property(self):
536
        tree = self.make_branch_and_tree('tree')
537
538
        tt = transform.TreeTransform(tree)
539
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
540
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
541
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
542
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
543
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
544
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
545
        tt.apply()
546
        tree.commit('one', rev_id='rev-1')
547
548
        tt = transform.TreeTransform(tree)
549
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
550
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
551
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
552
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
553
        tt.apply()
554
        tree.rename_one('c', 'new-c')
555
        tree.rename_one('d', 'new-d')
556
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
557
        d = get_diff_as_string(tree.basis_tree(), tree)
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
558
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
559
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
560
                                  ".*\+x to -x.*\)")
561
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
562
                                  ".*-x to \+x.*\)")
563
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
564
                                  ".*\+x to -x.*\)")
565
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
566
                                  ".*-x to \+x.*\)")
567
        self.assertNotContainsRe(d, r"file 'e'")
568
        self.assertNotContainsRe(d, r"file 'f'")
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
569
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
570
    def test_binary_unicode_filenames(self):
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
571
        """Test that contents of files are *not* encoded in UTF-8 when there
572
        is a binary file in the diff.
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
573
        """
574
        # See https://bugs.launchpad.net/bugs/110092.
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
575
        self.requireFeature(features.UnicodeFilenameFeature)
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
576
577
        # This bug isn't triggered with cStringIO.
578
        from StringIO import StringIO
579
        tree = self.make_branch_and_tree('tree')
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
580
        alpha, omega = u'\u03b1', u'\u03c9'
581
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
582
        self.build_tree_contents(
2592.2.2 by Jonathan Lange
Apply jam's comments to test_binary_unicode_filenames. Change the
583
            [('tree/' + alpha, chr(0)),
584
             ('tree/' + omega,
585
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
586
        tree.add([alpha], ['file-id'])
587
        tree.add([omega], ['file-id-2'])
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
588
        diff_content = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
589
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
590
        d = diff_content.getvalue()
591
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
592
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
593
                              % (alpha_utf8, alpha_utf8))
594
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
595
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
596
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
2592.2.1 by Jonathan Lange
Reproduce and fix bug 110092.
597
2725.2.1 by ghigo
When a unicode filename is renamed, in the diff is showed a wrong result
598
    def test_unicode_filename(self):
599
        """Test when the filename are unicode."""
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
600
        self.requireFeature(features.UnicodeFilenameFeature)
2725.2.1 by ghigo
When a unicode filename is renamed, in the diff is showed a wrong result
601
602
        alpha, omega = u'\u03b1', u'\u03c9'
603
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
604
605
        tree = self.make_branch_and_tree('tree')
606
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
607
        tree.add(['ren_'+alpha], ['file-id-2'])
608
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
609
        tree.add(['del_'+alpha], ['file-id-3'])
610
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
611
        tree.add(['mod_'+alpha], ['file-id-4'])
612
613
        tree.commit('one', rev_id='rev-1')
614
615
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
616
        tree.remove('del_'+alpha)
617
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
618
        tree.add(['add_'+alpha], ['file-id'])
619
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
620
5784.3.1 by Martin Pool
Remove unnecessary TestShowDiffTreesHelper and just use a function
621
        d = get_diff_as_string(tree.basis_tree(), tree)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
622
        self.assertContainsRe(d,
2725.2.1 by ghigo
When a unicode filename is renamed, in the diff is showed a wrong result
623
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
624
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
625
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
626
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
627
4797.57.6 by Alexander Belchenko
added whitebox test for path_encoding in diff.
628
    def test_unicode_filename_path_encoding(self):
629
        """Test for bug #382699: unicode filenames on Windows should be shown
630
        in user encoding.
631
        """
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
632
        self.requireFeature(features.UnicodeFilenameFeature)
4797.57.6 by Alexander Belchenko
added whitebox test for path_encoding in diff.
633
        # The word 'test' in Russian
634
        _russian_test = u'\u0422\u0435\u0441\u0442'
635
        directory = _russian_test + u'/'
636
        test_txt = _russian_test + u'.txt'
637
        u1234 = u'\u1234.txt'
638
639
        tree = self.make_branch_and_tree('.')
640
        self.build_tree_contents([
641
            (test_txt, 'foo\n'),
642
            (u1234, 'foo\n'),
643
            (directory, None),
644
            ])
645
        tree.add([test_txt, u1234, directory])
646
647
        sio = StringIO()
5258.1.1 by Alexander Belchenko
merge diff header work from my 2.1 branch
648
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
4797.57.6 by Alexander Belchenko
added whitebox test for path_encoding in diff.
649
            path_encoding='cp1251')
650
651
        output = subst_dates(sio.getvalue())
652
        shouldbe = ('''\
653
=== added directory '%(directory)s'
654
=== added file '%(test_txt)s'
655
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
656
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
657
@@ -0,0 +1,1 @@
658
+foo
659
660
=== added file '?.txt'
661
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
662
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
663
@@ -0,0 +1,1 @@
664
+foo
665
666
''' % {'directory': _russian_test.encode('cp1251'),
667
       'test_txt': test_txt.encode('cp1251'),
668
      })
669
        self.assertEqualDiff(output, shouldbe)
670
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
671
672
class DiffWasIs(diff.DiffPath):
3009.2.15 by Aaron Bentley
Test differ registration
673
674
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
675
        self.to_file.write('was: ')
676
        self.to_file.write(self.old_tree.get_file(file_id).read())
677
        self.to_file.write('is: ')
678
        self.to_file.write(self.new_tree.get_file(file_id).read())
679
        pass
680
681
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
682
class TestDiffTree(tests.TestCaseWithTransport):
3009.2.9 by Aaron Bentley
Add tests for Differ
683
684
    def setUp(self):
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
685
        super(TestDiffTree, self).setUp()
3009.2.9 by Aaron Bentley
Add tests for Differ
686
        self.old_tree = self.make_branch_and_tree('old-tree')
687
        self.old_tree.lock_write()
688
        self.addCleanup(self.old_tree.unlock)
689
        self.new_tree = self.make_branch_and_tree('new-tree')
690
        self.new_tree.lock_write()
691
        self.addCleanup(self.new_tree.unlock)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
692
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
3009.2.9 by Aaron Bentley
Add tests for Differ
693
694
    def test_diff_text(self):
695
        self.build_tree_contents([('old-tree/olddir/',),
696
                                  ('old-tree/olddir/oldfile', 'old\n')])
697
        self.old_tree.add('olddir')
698
        self.old_tree.add('olddir/oldfile', 'file-id')
699
        self.build_tree_contents([('new-tree/newdir/',),
700
                                  ('new-tree/newdir/newfile', 'new\n')])
701
        self.new_tree.add('newdir')
702
        self.new_tree.add('newdir/newfile', 'file-id')
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
703
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
704
        differ.diff_text('file-id', None, 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
705
        self.assertEqual(
706
            '--- 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
707
            differ.to_file.getvalue())
708
        differ.to_file.seek(0)
709
        differ.diff_text(None, 'file-id', 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
710
        self.assertEqual(
711
            '--- 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
712
            differ.to_file.getvalue())
713
        differ.to_file.seek(0)
714
        differ.diff_text('file-id', 'file-id', 'old label', 'new label')
3009.2.9 by Aaron Bentley
Add tests for Differ
715
        self.assertEqual(
716
            '--- 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
717
            differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
718
3087.1.1 by Aaron Bentley
Diff handles missing files correctly, with no tracebacks
719
    def test_diff_deletion(self):
720
        self.build_tree_contents([('old-tree/file', 'contents'),
721
                                  ('new-tree/file', 'contents')])
722
        self.old_tree.add('file', 'file-id')
723
        self.new_tree.add('file', 'file-id')
724
        os.unlink('new-tree/file')
725
        self.differ.show_diff(None)
726
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
727
728
    def test_diff_creation(self):
729
        self.build_tree_contents([('old-tree/file', 'contents'),
730
                                  ('new-tree/file', 'contents')])
731
        self.old_tree.add('file', 'file-id')
732
        self.new_tree.add('file', 'file-id')
733
        os.unlink('old-tree/file')
734
        self.differ.show_diff(None)
735
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
736
3009.2.9 by Aaron Bentley
Add tests for Differ
737
    def test_diff_symlink(self):
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
738
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
739
        differ.diff_symlink('old target', None)
3009.2.9 by Aaron Bentley
Add tests for Differ
740
        self.assertEqual("=== target was 'old target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
741
                         differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
742
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
743
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
744
        differ.diff_symlink(None, 'new target')
3009.2.9 by Aaron Bentley
Add tests for Differ
745
        self.assertEqual("=== target is 'new target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
746
                         differ.to_file.getvalue())
747
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
748
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
749
        differ.diff_symlink('old target', 'new target')
3009.2.9 by Aaron Bentley
Add tests for Differ
750
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
751
                         differ.to_file.getvalue())
3009.2.9 by Aaron Bentley
Add tests for Differ
752
753
    def test_diff(self):
754
        self.build_tree_contents([('old-tree/olddir/',),
755
                                  ('old-tree/olddir/oldfile', 'old\n')])
756
        self.old_tree.add('olddir')
757
        self.old_tree.add('olddir/oldfile', 'file-id')
758
        self.build_tree_contents([('new-tree/newdir/',),
759
                                  ('new-tree/newdir/newfile', 'new\n')])
760
        self.new_tree.add('newdir')
761
        self.new_tree.add('newdir/newfile', 'file-id')
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
762
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
3009.2.9 by Aaron Bentley
Add tests for Differ
763
        self.assertContainsRe(
764
            self.differ.to_file.getvalue(),
765
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
766
             ' \@\@\n-old\n\+new\n\n')
767
768
    def test_diff_kind_change(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
769
        self.requireFeature(features.SymlinkFeature)
3009.2.9 by Aaron Bentley
Add tests for Differ
770
        self.build_tree_contents([('old-tree/olddir/',),
771
                                  ('old-tree/olddir/oldfile', 'old\n')])
772
        self.old_tree.add('olddir')
773
        self.old_tree.add('olddir/oldfile', 'file-id')
774
        self.build_tree(['new-tree/newdir/'])
775
        os.symlink('new', 'new-tree/newdir/newfile')
776
        self.new_tree.add('newdir')
777
        self.new_tree.add('newdir/newfile', 'file-id')
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
778
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
3009.2.9 by Aaron Bentley
Add tests for Differ
779
        self.assertContainsRe(
780
            self.differ.to_file.getvalue(),
781
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
782
             ' \@\@\n-old\n\n')
783
        self.assertContainsRe(self.differ.to_file.getvalue(),
4216.3.1 by Robert Collins
Fix Tree.get_symlink_target to decode from the disk encoding to get a unicode encoded string.
784
                              "=== target is u'new'\n")
3009.2.9 by Aaron Bentley
Add tests for Differ
785
3009.2.19 by Aaron Bentley
Implement directory diffing
786
    def test_diff_directory(self):
787
        self.build_tree(['new-tree/new-dir/'])
788
        self.new_tree.add('new-dir', 'new-dir-id')
789
        self.differ.diff('new-dir-id', None, 'new-dir')
790
        self.assertEqual(self.differ.to_file.getvalue(), '')
791
3009.2.16 by Aaron Bentley
Test support for extra differs
792
    def create_old_new(self):
793
        self.build_tree_contents([('old-tree/olddir/',),
794
                                  ('old-tree/olddir/oldfile', 'old\n')])
795
        self.old_tree.add('olddir')
796
        self.old_tree.add('olddir/oldfile', 'file-id')
797
        self.build_tree_contents([('new-tree/newdir/',),
798
                                  ('new-tree/newdir/newfile', 'new\n')])
799
        self.new_tree.add('newdir')
800
        self.new_tree.add('newdir/newfile', 'file-id')
801
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
802
    def test_register_diff(self):
3009.2.16 by Aaron Bentley
Test support for extra differs
803
        self.create_old_new()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
804
        old_diff_factories = diff.DiffTree.diff_factories
805
        diff.DiffTree.diff_factories=old_diff_factories[:]
806
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
3009.2.16 by Aaron Bentley
Test support for extra differs
807
        try:
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
808
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
3009.2.16 by Aaron Bentley
Test support for extra differs
809
        finally:
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
810
            diff.DiffTree.diff_factories = old_diff_factories
3009.2.16 by Aaron Bentley
Test support for extra differs
811
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
812
        self.assertNotContainsRe(
813
            differ.to_file.getvalue(),
814
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
815
             ' \@\@\n-old\n\+new\n\n')
816
        self.assertContainsRe(differ.to_file.getvalue(),
817
                              'was: old\nis: new\n')
818
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
819
    def test_extra_factories(self):
3009.2.16 by Aaron Bentley
Test support for extra differs
820
        self.create_old_new()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
821
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
822
                               extra_factories=[DiffWasIs.from_diff_tree])
3009.2.16 by Aaron Bentley
Test support for extra differs
823
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
824
        self.assertNotContainsRe(
825
            differ.to_file.getvalue(),
826
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
827
             ' \@\@\n-old\n\+new\n\n')
828
        self.assertContainsRe(differ.to_file.getvalue(),
829
                              'was: old\nis: new\n')
830
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
831
    def test_alphabetical_order(self):
832
        self.build_tree(['new-tree/a-file'])
833
        self.new_tree.add('a-file')
834
        self.build_tree(['old-tree/b-file'])
835
        self.old_tree.add('b-file')
836
        self.differ.show_diff(None)
837
        self.assertContainsRe(self.differ.to_file.getvalue(),
838
            '.*a-file(.|\n)*b-file')
839
3009.2.9 by Aaron Bentley
Add tests for Differ
840
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
841
class TestPatienceDiffLib(tests.TestCase):
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
842
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
843
    def setUp(self):
844
        super(TestPatienceDiffLib, self).setUp()
5168.1.3 by Vincent Ladeuil
Even more import fixes.
845
        self._unique_lcs = _patiencediff_py.unique_lcs_py
846
        self._recurse_matches = _patiencediff_py.recurse_matches_py
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
847
        self._PatienceSequenceMatcher = \
5168.1.3 by Vincent Ladeuil
Even more import fixes.
848
            _patiencediff_py.PatienceSequenceMatcher_py
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
849
3628.1.3 by Lukáš Lalinský
Add a test
850
    def test_diff_unicode_string(self):
851
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
852
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
853
        sm = self._PatienceSequenceMatcher(None, a, b)
854
        mb = sm.get_matching_blocks()
855
        self.assertEquals(35, len(mb))
856
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
857
    def test_unique_lcs(self):
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
858
        unique_lcs = self._unique_lcs
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
859
        self.assertEquals(unique_lcs('', ''), [])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
860
        self.assertEquals(unique_lcs('', 'a'), [])
861
        self.assertEquals(unique_lcs('a', ''), [])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
862
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
863
        self.assertEquals(unique_lcs('a', 'b'), [])
864
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
865
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
866
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
867
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
868
                                                         (3,3), (4,4)])
869
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
870
871
    def test_recurse_matches(self):
872
        def test_one(a, b, matches):
873
            test_matches = []
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
874
            self._recurse_matches(
875
                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,
876
            self.assertEquals(test_matches, matches)
877
1711.2.17 by John Arbash Meinel
Small cleanups to patience_diff code.
878
        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,
879
                 [(0, 0), (2, 2), (4, 4)])
880
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
881
                 [(0, 0), (2, 1), (4, 2)])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
882
        # Even though 'bc' is not unique globally, and is surrounded by
883
        # non-matching lines, we should still match, because they are locally
884
        # unique
885
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
886
                                          (4, 6), (5, 7), (6, 8)])
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
887
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
888
        # recurse_matches doesn't match non-unique
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
889
        # lines surrounded by bogus text.
1185.81.24 by Aaron Bentley
Reoganize patience-related code
890
        # 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
891
892
        # This is what it could be
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
893
        #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
894
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
895
        # This is what it currently gives:
896
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
897
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
898
    def assertDiffBlocks(self, a, b, expected_blocks):
899
        """Check that the sequence matcher returns the correct blocks.
900
901
        :param a: A sequence to match
902
        :param b: Another sequence to match
903
        :param expected_blocks: The expected output, not including the final
904
            matching block (len(a), len(b), 0)
905
        """
906
        matcher = self._PatienceSequenceMatcher(None, a, b)
907
        blocks = matcher.get_matching_blocks()
908
        last = blocks.pop()
909
        self.assertEqual((len(a), len(b), 0), last)
910
        self.assertEqual(expected_blocks, blocks)
911
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
912
    def test_matching_blocks(self):
1185.81.2 by John Arbash Meinel
A couple small tests.
913
        # Some basic matching tests
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
914
        self.assertDiffBlocks('', '', [])
915
        self.assertDiffBlocks([], [], [])
916
        self.assertDiffBlocks('abc', '', [])
917
        self.assertDiffBlocks('', 'abc', [])
918
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
919
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
920
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
921
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
922
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
923
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
924
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
925
        # This may check too much, but it checks to see that
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
926
        # a copied block stays attached to the previous section,
927
        # not the later one.
928
        # difflib would tend to grab the trailing longest match
929
        # which would make the diff not look right
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
930
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
931
                              [(0, 0, 6), (6, 11, 10)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
932
1185.81.2 by John Arbash Meinel
A couple small tests.
933
        # make sure it supports passing in lists
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
934
        self.assertDiffBlocks(
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
935
                   ['hello there\n',
936
                    'world\n',
937
                    'how are you today?\n'],
938
                   ['hello there\n',
939
                    'how are you today?\n'],
1185.81.2 by John Arbash Meinel
A couple small tests.
940
                [(0, 0, 1), (2, 1, 1)])
1185.81.1 by John Arbash Meinel
Adding nofrillsprecisemerge's diff algorithm, wrapped in difflib.
941
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
942
        # non unique lines surrounded by non-matching lines
943
        # won't be found
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
944
        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.
945
946
        # But they only need to be locally unique
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
947
        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.
948
949
        # non unique blocks won't be matched
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
950
        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.
951
952
        # but locally unique ones will
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
953
        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.
954
                                              (5,4,1), (7,5,2), (10,8,1)])
955
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
956
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
957
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
958
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
1185.81.11 by John Arbash Meinel
Found some edge cases that weren't being matched.
959
3074.2.1 by John Arbash Meinel
Change the C PatienceDiff implementation to support arbitrary objects.
960
    def test_matching_blocks_tuples(self):
961
        # Some basic matching tests
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
962
        self.assertDiffBlocks([], [], [])
963
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
964
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
965
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
966
                              [('a',), ('b',), ('c,')],
967
                              [(0, 0, 3)])
968
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
969
                              [('a',), ('b',), ('d,')],
970
                              [(0, 0, 2)])
971
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
972
                              [('a',), ('b',), ('c,')],
973
                              [(1, 1, 2)])
974
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
975
                              [('a',), ('b',), ('c,')],
976
                              [(1, 0, 3)])
977
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
978
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
979
                              [(0, 0, 1), (2, 2, 1)])
980
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
981
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
982
                              [(0, 0, 1), (2, 2, 1)])
3074.2.1 by John Arbash Meinel
Change the C PatienceDiff implementation to support arbitrary objects.
983
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
984
    def test_opcodes(self):
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
985
        def chk_ops(a, b, expected_codes):
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
986
            s = self._PatienceSequenceMatcher(None, a, b)
1711.2.10 by John Arbash Meinel
Clarify the patience tests a little bit.
987
            self.assertEquals(expected_codes, s.get_opcodes())
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
988
989
        chk_ops('', '', [])
990
        chk_ops([], [], [])
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
991
        chk_ops('abc', '', [('delete', 0,3, 0,0)])
992
        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,
993
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
994
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
995
                                 ('replace', 3,4, 3,4)
996
                                ])
997
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
998
                                 ('equal',  1,4, 0,3),
999
                                 ('insert', 4,4, 3,4)
1000
                                ])
1001
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
1002
                                  ('equal',  1,5, 0,4)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1003
                                 ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1004
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
1005
                                   ('replace', 2,3, 2,3),
1006
                                   ('equal',   3,5, 3,5)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1007
                                  ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1008
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
1009
                                     ('replace', 2,3, 2,5),
1010
                                     ('equal',   3,5, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1011
                                    ])
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1012
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
1013
                                    ('insert', 2,2, 2,5),
1014
                                    ('equal',  2,4, 5,7)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1015
                                   ])
1016
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1017
                [('equal',  0,6,  0,6),
1018
                 ('insert', 6,6,  6,11),
1019
                 ('equal',  6,16, 11,21)
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1020
                ])
1021
        chk_ops(
1022
                [ 'hello there\n'
1023
                , 'world\n'
1024
                , 'how are you today?\n'],
1025
                [ 'hello there\n'
1026
                , 'how are you today?\n'],
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1027
                [('equal',  0,1, 0,1),
1028
                 ('delete', 1,2, 1,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1029
                 ('equal',  2,3, 1,2),
1185.81.9 by John Arbash Meinel
Added (failing) tests for cdv.recurse_matches with common sections,
1030
                ])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1031
        chk_ops('aBccDe', 'abccde',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1032
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1033
                 ('replace', 1,5, 1,5),
1034
                 ('equal',   5,6, 5,6),
1035
                ])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1036
        chk_ops('aBcDec', 'abcdec',
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1037
                [('equal',   0,1, 0,1),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1038
                 ('replace', 1,2, 1,2),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1039
                 ('equal',   2,3, 2,3),
1040
                 ('replace', 3,4, 3,4),
1041
                 ('equal',   4,6, 4,6),
1185.81.3 by John Arbash Meinel
Adding tests for checking opcodes.
1042
                ])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1043
        chk_ops('aBcdEcdFg', 'abcdecdfg',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1044
                [('equal',   0,1, 0,1),
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1045
                 ('replace', 1,8, 1,8),
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1046
                 ('equal',   8,9, 8,9)
1185.81.10 by John Arbash Meinel
Added some more test cases.
1047
                ])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1048
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
1711.2.21 by John Arbash Meinel
Cleanup patiencediff, remove the use of difflib.SequenceMatcher.
1049
                [('equal',   0,1, 0,1),
1050
                 ('replace', 1,2, 1,2),
1051
                 ('equal',   2,4, 2,4),
1052
                 ('delete', 4,5, 4,4),
1053
                 ('equal',   5,6, 4,5),
1054
                 ('delete', 6,7, 5,5),
1055
                 ('equal',   7,9, 5,7),
1056
                 ('replace', 9,10, 7,8),
1057
                 ('equal',   10,11, 8,9)
1058
                ])
1185.81.10 by John Arbash Meinel
Added some more test cases.
1059
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1060
    def test_grouped_opcodes(self):
1061
        def chk_ops(a, b, expected_codes, n=3):
1062
            s = self._PatienceSequenceMatcher(None, a, b)
1063
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
1064
1065
        chk_ops('', '', [])
1066
        chk_ops([], [], [])
1067
        chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
1068
        chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
1069
        chk_ops('abcd', 'abcd', [])
1070
        chk_ops('abcd', 'abce', [[('equal',   0,3, 0,3),
1071
                                  ('replace', 3,4, 3,4)
1072
                                 ]])
1073
        chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
1074
                                 ('equal',  1,4, 0,3),
1075
                                 ('insert', 4,4, 3,4)
1076
                                ]])
1077
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1078
                [[('equal',  3,6, 3,6),
1079
                  ('insert', 6,6, 6,11),
1080
                  ('equal',  6,9, 11,14)
1081
                  ]])
1082
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
1083
                [[('equal',  2,6, 2,6),
1084
                  ('insert', 6,6, 6,11),
1085
                  ('equal',  6,10, 11,15)
1086
                  ]], 4)
1087
        chk_ops('Xabcdef', 'abcdef',
1088
                [[('delete', 0,1, 0,0),
1089
                  ('equal',  1,4, 0,3)
1090
                  ]])
1091
        chk_ops('abcdef', 'abcdefX',
1092
                [[('equal',  3,6, 3,6),
1093
                  ('insert', 6,6, 6,7)
1094
                  ]])
1095
1096
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
1097
    def test_multiple_ranges(self):
1098
        # There was an earlier bug where we used a bad set of ranges,
1099
        # this triggers that specific bug, to make sure it doesn't regress
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1100
        self.assertDiffBlocks('abcdefghijklmnop',
1101
                              'abcXghiYZQRSTUVWXYZijklmnop',
1102
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
1103
1104
        self.assertDiffBlocks('ABCd efghIjk  L',
1105
                              'AxyzBCn mo pqrstuvwI1 2  L',
1106
                              [(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.
1107
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1108
        # These are rot13 code snippets.
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1109
        self.assertDiffBlocks('''\
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1110
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1111
    """
1112
    gnxrf_netf = ['svyr*']
1113
    gnxrf_bcgvbaf = ['ab-erphefr']
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1114
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1115
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1116
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1117
        vs vf_dhvrg():
1118
            ercbegre = nqq_ercbegre_ahyy
1119
        ryfr:
1120
            ercbegre = nqq_ercbegre_cevag
1121
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
1122
1123
1124
pynff pzq_zxqve(Pbzznaq):
1125
'''.splitlines(True), '''\
1126
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1127
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1128
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1711.2.8 by John Arbash Meinel
rot13 the code snippet to help with clarity.
1129
    nqq gurz.
1130
    """
1131
    gnxrf_netf = ['svyr*']
1132
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
1133
1134
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
1135
        vzcbeg omeyvo.nqq
1136
1137
        vs qel_eha:
1138
            vs vf_dhvrg():
1139
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
1140
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
1141
            ryfr:
1142
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
1143
        ryvs vf_dhvrg():
1144
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
1145
        ryfr:
1146
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
1147
1148
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
1149
1150
1151
pynff pzq_zxqve(Pbzznaq):
1185.81.16 by John Arbash Meinel
Added tests, and an assert check to make sure ranges are always increasing.
1152
'''.splitlines(True)
1153
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1154
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1155
    def test_patience_unified_diff(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1156
        txt_a = ['hello there\n',
1157
                 'world\n',
1158
                 'how are you today?\n']
1159
        txt_b = ['hello there\n',
1160
                 'how are you today?\n']
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1161
        unified_diff = patiencediff.unified_diff
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1162
        psm = self._PatienceSequenceMatcher
3922.1.3 by John Arbash Meinel
fix some odd spacing.
1163
        self.assertEquals(['--- \n',
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1164
                           '+++ \n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1165
                           '@@ -1,3 +1,2 @@\n',
1166
                           ' hello there\n',
1167
                           '-world\n',
1168
                           ' 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
1169
                          ]
1711.2.20 by John Arbash Meinel
Late bind to patiencediff objects to make it easier to plug-in
1170
                          , list(unified_diff(txt_a, txt_b,
1171
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1172
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1173
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1174
        # This is the result with LongestCommonSubstring matching
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1175
        self.assertEquals(['--- \n',
1176
                           '+++ \n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1177
                           '@@ -1,6 +1,11 @@\n',
1178
                           ' a\n',
1179
                           ' b\n',
1180
                           ' c\n',
1181
                           '+d\n',
1182
                           '+e\n',
1183
                           '+f\n',
1184
                           '+x\n',
1185
                           '+y\n',
1186
                           ' d\n',
1187
                           ' e\n',
1188
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1189
                          , list(unified_diff(txt_a, txt_b)))
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1190
        # And the patience diff
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1191
        self.assertEquals(['--- \n',
1192
                           '+++ \n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1193
                           '@@ -4,6 +4,11 @@\n',
1194
                           ' d\n',
1195
                           ' e\n',
1196
                           ' f\n',
1197
                           '+x\n',
1198
                           '+y\n',
1199
                           '+d\n',
1200
                           '+e\n',
1201
                           '+f\n',
1202
                           ' g\n',
1203
                           ' h\n',
1204
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1205
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1206
                          , 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
1207
                                 sequencematcher=psm)))
1185.81.25 by Aaron Bentley
Clean up test_diff
1208
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1209
    def test_patience_unified_diff_with_dates(self):
1210
        txt_a = ['hello there\n',
1211
                 'world\n',
1212
                 'how are you today?\n']
1213
        txt_b = ['hello there\n',
1214
                 'how are you today?\n']
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1215
        unified_diff = patiencediff.unified_diff
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1216
        psm = self._PatienceSequenceMatcher
3922.1.4 by John Arbash Meinel
It turns out that internal_diff worked around the trailing whitespace problem
1217
        self.assertEquals(['--- a\t2008-08-08\n',
1218
                           '+++ b\t2008-09-09\n',
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1219
                           '@@ -1,3 +1,2 @@\n',
1220
                           ' hello there\n',
1221
                           '-world\n',
1222
                           ' how are you today?\n'
1223
                          ]
1224
                          , list(unified_diff(txt_a, txt_b,
1225
                                 fromfile='a', tofile='b',
1226
                                 fromfiledate='2008-08-08',
1227
                                 tofiledate='2008-09-09',
1228
                                 sequencematcher=psm)))
1229
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1230
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1231
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1232
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1233
    _test_needs_features = [features.compiled_patiencediff_feature]
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1234
1235
    def setUp(self):
1236
        super(TestPatienceDiffLib_c, self).setUp()
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1237
        from bzrlib import _patiencediff_c
1238
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1239
        self._recurse_matches = _patiencediff_c.recurse_matches_c
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1240
        self._PatienceSequenceMatcher = \
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1241
            _patiencediff_c.PatienceSequenceMatcher_c
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1242
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1243
    def test_unhashable(self):
1244
        """We should get a proper exception here."""
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1245
        # We need to be able to hash items in the sequence, lists are
1246
        # unhashable, and thus cannot be diffed
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1247
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1248
                                         None, [[]], [])
3074.2.10 by John Arbash Meinel
Cleanup the test cases (Andrew)
1249
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1250
                                         None, ['valid', []], [])
1251
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1252
                                         None, ['valid'], [[]])
1253
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
1254
                                         None, ['valid'], ['valid', []])
3074.2.3 by John Arbash Meinel
Enable some error checking, and small amount of code cleanup.
1255
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1256
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1257
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1258
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1259
    def setUp(self):
1260
        super(TestPatienceDiffLibFiles, self).setUp()
1261
        self._PatienceSequenceMatcher = \
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1262
            _patiencediff_py.PatienceSequenceMatcher_py
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1263
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1264
    def test_patience_unified_diff_files(self):
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1265
        txt_a = ['hello there\n',
1266
                 'world\n',
1267
                 'how are you today?\n']
1268
        txt_b = ['hello there\n',
1269
                 'how are you today?\n']
6437.20.3 by Wouter van Heyst
mechanically replace file().write() pattern with a with-keyword version
1270
        with open('a1', 'wb') as f: f.writelines(txt_a)
1271
        with open('b1', 'wb') as f: f.writelines(txt_b)
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1272
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1273
        unified_diff_files = patiencediff.unified_diff_files
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1274
        psm = self._PatienceSequenceMatcher
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1275
        self.assertEquals(['--- a1\n',
1276
                           '+++ b1\n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1277
                           '@@ -1,3 +1,2 @@\n',
1278
                           ' hello there\n',
1279
                           '-world\n',
1280
                           ' 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
1281
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1282
                          , 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
1283
                                 sequencematcher=psm)))
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1284
1285
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1286
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
6437.20.3 by Wouter van Heyst
mechanically replace file().write() pattern with a with-keyword version
1287
        with open('a2', 'wb') as f: f.writelines(txt_a)
1288
        with open('b2', 'wb') as f: f.writelines(txt_b)
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1289
1290
        # This is the result with LongestCommonSubstring matching
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1291
        self.assertEquals(['--- a2\n',
1292
                           '+++ b2\n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1293
                           '@@ -1,6 +1,11 @@\n',
1294
                           ' a\n',
1295
                           ' b\n',
1296
                           ' c\n',
1297
                           '+d\n',
1298
                           '+e\n',
1299
                           '+f\n',
1300
                           '+x\n',
1301
                           '+y\n',
1302
                           ' d\n',
1303
                           ' e\n',
1304
                           ' f\n']
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1305
                          , list(unified_diff_files('a2', 'b2')))
1306
1711.2.9 by John Arbash Meinel
Rename cdv => patience
1307
        # And the patience diff
3922.1.2 by John Arbash Meinel
Update the test cases for the new patience diff code.
1308
        self.assertEquals(['--- a2\n',
1309
                           '+++ b2\n',
1185.81.29 by Aaron Bentley
Fix style issues and duplicated tests
1310
                           '@@ -4,6 +4,11 @@\n',
1311
                           ' d\n',
1312
                           ' e\n',
1313
                           ' f\n',
1314
                           '+x\n',
1315
                           '+y\n',
1316
                           '+d\n',
1317
                           '+e\n',
1318
                           '+f\n',
1319
                           ' g\n',
1320
                           ' h\n',
1321
                           ' i\n',
1185.81.14 by John Arbash Meinel
Added a main function for running cdvdifflib manually, included tests for unified_diff interfaces
1322
                          ]
1185.81.25 by Aaron Bentley
Clean up test_diff
1323
                          , 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
1324
                                 sequencematcher=psm)))
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1325
1326
1327
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1328
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1329
    _test_needs_features = [features.compiled_patiencediff_feature]
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1330
1331
    def setUp(self):
1332
        super(TestPatienceDiffLibFiles_c, self).setUp()
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1333
        from bzrlib import _patiencediff_c
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1334
        self._PatienceSequenceMatcher = \
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1335
            _patiencediff_c.PatienceSequenceMatcher_c
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1336
1337
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1338
class TestUsingCompiledIfAvailable(tests.TestCase):
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1339
1340
    def test_PatienceSequenceMatcher(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1341
        if features.compiled_patiencediff_feature.available():
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1342
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1343
            self.assertIs(PatienceSequenceMatcher_c,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1344
                          patiencediff.PatienceSequenceMatcher)
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1345
        else:
1346
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1347
            self.assertIs(PatienceSequenceMatcher_py,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1348
                          patiencediff.PatienceSequenceMatcher)
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1349
1350
    def test_unique_lcs(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1351
        if features.compiled_patiencediff_feature.available():
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1352
            from bzrlib._patiencediff_c import unique_lcs_c
1353
            self.assertIs(unique_lcs_c,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1354
                          patiencediff.unique_lcs)
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1355
        else:
1356
            from bzrlib._patiencediff_py import unique_lcs_py
1357
            self.assertIs(unique_lcs_py,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1358
                          patiencediff.unique_lcs)
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1359
1360
    def test_recurse_matches(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1361
        if features.compiled_patiencediff_feature.available():
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1362
            from bzrlib._patiencediff_c import recurse_matches_c
1363
            self.assertIs(recurse_matches_c,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1364
                          patiencediff.recurse_matches)
2781.1.1 by Martin Pool
merge cpatiencediff from Lukas
1365
        else:
1366
            from bzrlib._patiencediff_py import recurse_matches_py
1367
            self.assertIs(recurse_matches_py,
5168.1.3 by Vincent Ladeuil
Even more import fixes.
1368
                          patiencediff.recurse_matches)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1369
1370
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1371
class TestDiffFromTool(tests.TestCaseWithTransport):
3123.6.2 by Aaron Bentley
Implement diff --using natively
1372
1373
    def test_from_string(self):
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1374
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1375
        self.addCleanup(diff_obj.finish)
4603.1.20 by Aaron Bentley
Use string.Template substitution with @ as delimiter.
1376
        self.assertEqual(['diff', '@old_path', '@new_path'],
3123.6.2 by Aaron Bentley
Implement diff --using natively
1377
            diff_obj.command_template)
3199.1.6 by Vincent Ladeuil
Fiz last leaking tmp dir.
1378
1379
    def test_from_string_u5(self):
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1380
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1381
                                                 None, None, None)
3199.1.6 by Vincent Ladeuil
Fiz last leaking tmp dir.
1382
        self.addCleanup(diff_obj.finish)
4603.1.20 by Aaron Bentley
Use string.Template substitution with @ as delimiter.
1383
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
3123.6.2 by Aaron Bentley
Implement diff --using natively
1384
                         diff_obj.command_template)
1385
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1386
                         diff_obj._get_command('old-path', 'new-path'))
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1387
4913.5.8 by Gordon Tyler
Added test_from_string_path_with_backslashes, which tests the actual scenario in bug 392428.
1388
    def test_from_string_path_with_backslashes(self):
5241.2.2 by Robert Collins
Missed one test.
1389
        self.requireFeature(features.backslashdir_feature)
4913.5.8 by Gordon Tyler
Added test_from_string_path_with_backslashes, which tests the actual scenario in bug 392428.
1390
        tool = 'C:\\Tools\\Diff.exe'
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1391
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
4913.5.8 by Gordon Tyler
Added test_from_string_path_with_backslashes, which tests the actual scenario in bug 392428.
1392
        self.addCleanup(diff_obj.finish)
1393
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
1394
                         diff_obj.command_template)
1395
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1396
                         diff_obj._get_command('old-path', 'new-path'))
3123.6.2 by Aaron Bentley
Implement diff --using natively
1397
1398
    def test_execute(self):
1399
        output = StringIO()
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1400
        diff_obj = diff.DiffFromTool(['python', '-c',
1401
                                      'print "@old_path @new_path"'],
1402
                                     None, None, output)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1403
        self.addCleanup(diff_obj.finish)
1404
        diff_obj._execute('old', 'new')
3146.4.2 by Aaron Bentley
Avoid assuming unix newline on output
1405
        self.assertEqual(output.getvalue().rstrip(), 'old new')
3123.6.2 by Aaron Bentley
Implement diff --using natively
1406
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
1407
    def test_execute_missing(self):
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1408
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1409
                                     None, None, None)
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
1410
        self.addCleanup(diff_obj.finish)
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1411
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
1412
                              'old', 'new')
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
1413
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1414
                         ' on this machine', str(e))
1415
3287.18.22 by Matt McClure
Reverts to prior decomposition of exercise and verification, as suggested
1416
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
1417
        self.requireFeature(features.AttribFeature)
3287.18.10 by Matt McClure
Uses TestSkipped for test_execute_windows_tool on non-Windows platforms.
1418
        output = StringIO()
1419
        tree = self.make_branch_and_tree('tree')
1420
        self.build_tree_contents([('tree/file', 'content')])
1421
        tree.add('file', 'file-id')
3287.18.11 by Matt McClure
Removed unnecessary timestamp parameter.
1422
        tree.commit('old tree')
3287.18.10 by Matt McClure
Uses TestSkipped for test_execute_windows_tool on non-Windows platforms.
1423
        tree.lock_read()
1424
        self.addCleanup(tree.unlock)
4873.3.1 by John Arbash Meinel
Now that we return files directly from the working tree
1425
        basis_tree = tree.basis_tree()
1426
        basis_tree.lock_read()
1427
        self.addCleanup(basis_tree.unlock)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1428
        diff_obj = diff.DiffFromTool(['python', '-c',
1429
                                      'print "@old_path @new_path"'],
1430
                                     basis_tree, tree, output)
3287.18.22 by Matt McClure
Reverts to prior decomposition of exercise and verification, as suggested
1431
        diff_obj._prepare_files('file-id', 'file', 'file')
4873.3.1 by John Arbash Meinel
Now that we return files directly from the working tree
1432
        # The old content should be readonly
1433
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1434
                                    r'R.*old\\file$')
1435
        # The new content should use the tree object, not a 'new' file anymore
1436
        self.assertEndsWith(tree.basedir, 'work/tree')
1437
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
3287.18.22 by Matt McClure
Reverts to prior decomposition of exercise and verification, as suggested
1438
1439
    def assertReadableByAttrib(self, cwd, relpath, regex):
1440
        proc = subprocess.Popen(['attrib', relpath],
1441
                                stdout=subprocess.PIPE,
1442
                                cwd=cwd)
4873.3.1 by John Arbash Meinel
Now that we return files directly from the working tree
1443
        (result, err) = proc.communicate()
1444
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
3287.18.9 by Matt McClure
Adds a test asserting that a Windows tool that understands forward slashes
1445
3123.6.2 by Aaron Bentley
Implement diff --using natively
1446
    def test_prepare_files(self):
1447
        output = StringIO()
1448
        tree = self.make_branch_and_tree('tree')
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1449
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
3287.18.23 by Matt McClure
Adds comments that document my understanding of
1450
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1451
        tree.add('oldname', 'file-id')
3287.18.23 by Matt McClure
Adds comments that document my understanding of
1452
        tree.add('oldname2', 'file2-id')
5151.3.1 by Martin
Fix os.utime test failures, three on FAT filesystems and one with readonly files
1453
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1454
        tree.commit('old tree', timestamp=315532800)
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1455
        tree.rename_one('oldname', 'newname')
3287.18.23 by Matt McClure
Adds comments that document my understanding of
1456
        tree.rename_one('oldname2', 'newname2')
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1457
        self.build_tree_contents([('tree/newname', 'newcontent')])
3287.19.1 by Matt McClure
Fixes https://bugs.launchpad.net/bzr/+bug/212289. I submitted this patch.
1458
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
3123.6.2 by Aaron Bentley
Implement diff --using natively
1459
        old_tree = tree.basis_tree()
1460
        old_tree.lock_read()
1461
        self.addCleanup(old_tree.unlock)
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
1462
        tree.lock_read()
1463
        self.addCleanup(tree.unlock)
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1464
        diff_obj = diff.DiffFromTool(['python', '-c',
1465
                                      'print "@old_path @new_path"'],
1466
                                     old_tree, tree, output)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1467
        self.addCleanup(diff_obj.finish)
1468
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1469
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1470
                                                     'newname')
1471
        self.assertContainsRe(old_path, 'old/oldname$')
5151.3.1 by Martin
Fix os.utime test failures, three on FAT filesystems and one with readonly files
1472
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
4845.2.1 by Gary van der Merwe
When launching an external diff app, don't write temporary files for a working tree.
1473
        self.assertContainsRe(new_path, 'tree/newname$')
3123.6.2 by Aaron Bentley
Implement diff --using natively
1474
        self.assertFileEqual('oldcontent', old_path)
1475
        self.assertFileEqual('newcontent', new_path)
3287.18.14 by Matt McClure
Extracted a host_os_dereferences_symlinks method.
1476
        if osutils.host_os_dereferences_symlinks():
3123.6.5 by Aaron Bentley
Symlink to real files if possible
1477
            self.assertTrue(os.path.samefile('tree/newname', new_path))
3123.6.2 by Aaron Bentley
Implement diff --using natively
1478
        # make sure we can create files with the same parent directories
3287.18.25 by Matt McClure
Uses the correct file_id as the argument to _prepare_files.
1479
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1480
1481
5074.5.4 by INADA Naoki
fix easy bug.
1482
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
5074.5.2 by INADA Naoki
Add test for encoded filenames
1483
1484
    def test_encodable_filename(self):
5074.5.9 by INADA Naoki
Make additional comments to clarify
1485
        # Just checks file path for external diff tool.
1486
        # We cannot change CPython's internal encoding used by os.exec*.
5074.5.2 by INADA Naoki
Add test for encoded filenames
1487
        import sys
5074.5.9 by INADA Naoki
Make additional comments to clarify
1488
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1489
                                    None, None, None)
5074.5.2 by INADA Naoki
Add test for encoded filenames
1490
        for _, scenario in EncodingAdapter.encoding_scenarios:
1491
            encoding = scenario['encoding']
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1492
            dirname  = scenario['info']['directory']
5074.5.2 by INADA Naoki
Add test for encoded filenames
1493
            filename = scenario['info']['filename']
5074.5.6 by INADA Naoki
Change directry name for each check.
1494
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
1495
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1496
            relpath = dirname + u'/' + filename
1497
            fullpath = diffobj._safe_filename('safe', relpath)
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1498
            self.assertEqual(
1499
                    fullpath,
1500
                    fullpath.encode(encoding).decode(encoding)
1501
                    )
1502
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
5074.5.2 by INADA Naoki
Add test for encoded filenames
1503
1504
    def test_unencodable_filename(self):
1505
        import sys
5074.5.9 by INADA Naoki
Make additional comments to clarify
1506
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1507
                                    None, None, None)
5074.5.2 by INADA Naoki
Add test for encoded filenames
1508
        for _, scenario in EncodingAdapter.encoding_scenarios:
1509
            encoding = scenario['encoding']
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1510
            dirname  = scenario['info']['directory']
5074.5.2 by INADA Naoki
Add test for encoded filenames
1511
            filename = scenario['info']['filename']
1512
1513
            if encoding == 'iso-8859-1':
1514
                encoding = 'iso-8859-2'
1515
            else:
1516
                encoding = 'iso-8859-1'
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1517
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
1518
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1519
            relpath = dirname + u'/' + filename
1520
            fullpath = diffobj._safe_filename('safe', relpath)
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
1521
            self.assertEqual(
1522
                    fullpath,
1523
                    fullpath.encode(encoding).decode(encoding)
1524
                    )
1525
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
5074.5.2 by INADA Naoki
Add test for encoded filenames
1526
1527
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1528
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
5147.3.7 by Andrew Bennetts
Expect DeprecationWarnings for get_trees_and_branches_to_diff in test_diff, and add corresponding test coverage for get_trees_and_branches_to_diff_locked.
1529
1530
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
6027.1.4 by Vincent Ladeuil
Remove ``diff.get_trees_and_branches_to_diff`` deprecated in 2.2.0 and the corrsponding tests.
1531
        """Call get_trees_and_branches_to_diff_locked."""
5168.1.4 by Vincent Ladeuil
Final import fixes for bt.test_diff.
1532
        return diff.get_trees_and_branches_to_diff_locked(
5147.3.7 by Andrew Bennetts
Expect DeprecationWarnings for get_trees_and_branches_to_diff in test_diff, and add corresponding test coverage for get_trees_and_branches_to_diff_locked.
1533
            path_list, revision_specs, old_url, new_url, self.addCleanup)
4732.1.2 by Vincent Ladeuil
(trivial) Fix some PEP8 issues
1534
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1535
    def test_basic(self):
1536
        tree = self.make_branch_and_tree('tree')
1537
        (old_tree, new_tree,
1538
         old_branch, new_branch,
5147.3.7 by Andrew Bennetts
Expect DeprecationWarnings for get_trees_and_branches_to_diff in test_diff, and add corresponding test coverage for get_trees_and_branches_to_diff_locked.
1539
         specific_files, extra_trees) = self.call_gtabtd(
1540
             ['tree'], None, None, None)
4732.1.2 by Vincent Ladeuil
(trivial) Fix some PEP8 issues
1541
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1542
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1543
        self.assertEqual(_mod_revision.NULL_REVISION,
1544
                         old_tree.get_revision_id())
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1545
        self.assertEqual(tree.basedir, new_tree.basedir)
1546
        self.assertEqual(tree.branch.base, old_branch.base)
1547
        self.assertEqual(tree.branch.base, new_branch.base)
1548
        self.assertIs(None, specific_files)
1549
        self.assertIs(None, extra_trees)
1550
1551
    def test_with_rev_specs(self):
1552
        tree = self.make_branch_and_tree('tree')
1553
        self.build_tree_contents([('tree/file', 'oldcontent')])
1554
        tree.add('file', 'file-id')
1555
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1556
        self.build_tree_contents([('tree/file', 'newcontent')])
1557
        tree.commit('new tree', timestamp=0, rev_id="new-id")
4732.1.2 by Vincent Ladeuil
(trivial) Fix some PEP8 issues
1558
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1559
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1560
                     revisionspec.RevisionSpec.from_string('2')]
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1561
        (old_tree, new_tree,
1562
         old_branch, new_branch,
5147.3.7 by Andrew Bennetts
Expect DeprecationWarnings for get_trees_and_branches_to_diff in test_diff, and add corresponding test coverage for get_trees_and_branches_to_diff_locked.
1563
         specific_files, extra_trees) = self.call_gtabtd(
1564
            ['tree'], revisions, None, None)
4732.1.2 by Vincent Ladeuil
(trivial) Fix some PEP8 issues
1565
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1566
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1567
        self.assertEqual("old-id", old_tree.get_revision_id())
5168.1.2 by Vincent Ladeuil
Ckeanup some more imports.
1568
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
4705.1.2 by Gary van der Merwe
Start on tests for get_trees_and_branches_to_diff.
1569
        self.assertEqual("new-id", new_tree.get_revision_id())
1570
        self.assertEqual(tree.branch.base, old_branch.base)
1571
        self.assertEqual(tree.branch.base, new_branch.base)
1572
        self.assertIs(None, specific_files)
4705.1.4 by Gary van der Merwe
Add newline to end of test_diff.py
1573
        self.assertEqual(tree.basedir, extra_trees[0].basedir)