/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_diff.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import re
19
18
import subprocess
20
 
import sys
21
19
import tempfile
22
20
 
23
21
from .. import (
24
 
    cleanup,
25
22
    diff,
26
23
    errors,
27
24
    osutils,
 
25
    patiencediff,
 
26
    _patiencediff_py,
28
27
    revision as _mod_revision,
29
28
    revisionspec,
30
29
    revisiontree,
31
30
    tests,
 
31
    transform,
32
32
    )
33
33
from ..sixish import (
34
34
    BytesIO,
35
 
    unichr,
36
35
    )
37
36
from ..tests import (
38
37
    features,
39
38
    EncodingAdapter,
40
39
    )
 
40
from ..tests.blackbox.test_diff import subst_dates
41
41
from ..tests.scenarios import load_tests_apply_scenarios
42
42
 
43
43
 
44
44
load_tests = load_tests_apply_scenarios
45
45
 
46
46
 
47
 
def subst_dates(string):
48
 
    """Replace date strings with constant values."""
49
 
    return re.sub(br'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
50
 
                  b'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
51
 
 
52
 
 
53
47
def udiff_lines(old, new, allow_binary=False):
54
48
    output = BytesIO()
55
49
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
103
97
class TestDiffOptionsScenarios(tests.TestCase):
104
98
 
105
99
    scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
106
 
    style = None  # Set by load_tests_apply_scenarios from scenarios
 
100
    style = None # Set by load_tests_apply_scenarios from scenarios
107
101
 
108
102
    def test_unified_not_added(self):
109
103
        # Verify that for all valid style options, '-u' is not
116
110
 
117
111
    def test_add_nl(self):
118
112
        """diff generates a valid diff for patches that add a newline"""
119
 
        lines = udiff_lines([b'boo'], [b'boo\n'])
 
113
        lines = udiff_lines(['boo'], ['boo\n'])
120
114
        self.check_patch(lines)
121
 
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
122
 
        ## "expected no-nl, got %r" % lines[4]
 
115
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
 
116
            ## "expected no-nl, got %r" % lines[4]
123
117
 
124
118
    def test_add_nl_2(self):
125
119
        """diff generates a valid diff for patches that change last line and
126
120
        add a newline.
127
121
        """
128
 
        lines = udiff_lines([b'boo'], [b'goo\n'])
 
122
        lines = udiff_lines(['boo'], ['goo\n'])
129
123
        self.check_patch(lines)
130
 
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
131
 
        ## "expected no-nl, got %r" % lines[4]
 
124
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
 
125
            ## "expected no-nl, got %r" % lines[4]
132
126
 
133
127
    def test_remove_nl(self):
134
128
        """diff generates a valid diff for patches that change last line and
135
129
        add a newline.
136
130
        """
137
 
        lines = udiff_lines([b'boo\n'], [b'boo'])
 
131
        lines = udiff_lines(['boo\n'], ['boo'])
138
132
        self.check_patch(lines)
139
 
        self.assertEqual(lines[5], b'\\ No newline at end of file\n')
140
 
        ## "expected no-nl, got %r" % lines[5]
 
133
        self.assertEqual(lines[5], '\\ No newline at end of file\n')
 
134
            ## "expected no-nl, got %r" % lines[5]
141
135
 
142
136
    def check_patch(self, lines):
143
137
        self.assertTrue(len(lines) > 1)
144
 
        ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
145
 
        self.assertTrue(lines[0].startswith(b'---'))
146
 
        ## 'No orig line for patch:\n%s' % "".join(lines)
147
 
        self.assertTrue(lines[1].startswith(b'+++'))
148
 
        ## 'No mod line for patch:\n%s' % "".join(lines)
 
138
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
 
139
        self.assertTrue(lines[0].startswith ('---'))
 
140
            ## 'No orig line for patch:\n%s' % "".join(lines)
 
141
        self.assertTrue(lines[1].startswith ('+++'))
 
142
            ## 'No mod line for patch:\n%s' % "".join(lines)
149
143
        self.assertTrue(len(lines) > 2)
150
 
        ## "No hunks for patch:\n%s" % "".join(lines)
151
 
        self.assertTrue(lines[2].startswith(b'@@'))
152
 
        ## "No hunk header for patch:\n%s" % "".join(lines)
153
 
        self.assertTrue(b'@@' in lines[2][2:])
154
 
        ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
 
144
            ## "No hunks for patch:\n%s" % "".join(lines)
 
145
        self.assertTrue(lines[2].startswith('@@'))
 
146
            ## "No hunk header for patch:\n%s" % "".join(lines)
 
147
        self.assertTrue('@@' in lines[2][2:])
 
148
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
155
149
 
156
150
    def test_binary_lines(self):
157
151
        empty = []
158
 
        uni_lines = [1023 * b'a' + b'\x00']
 
152
        uni_lines = [1023 * 'a' + '\x00']
159
153
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
160
154
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
161
155
        udiff_lines(uni_lines, empty, allow_binary=True)
162
156
        udiff_lines(empty, uni_lines, allow_binary=True)
163
157
 
164
158
    def test_external_diff(self):
165
 
        lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
 
159
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
166
160
        self.check_patch(lines)
167
 
        self.assertEqual(b'\n', lines[-1])
 
161
        self.assertEqual('\n', lines[-1])
168
162
 
169
163
    def test_external_diff_no_fileno(self):
170
164
        # Make sure that we can handle not having a fileno, even
171
165
        # if the diff is large
172
 
        lines = external_udiff_lines([b'boo\n'] * 10000,
173
 
                                     [b'goo\n'] * 10000,
 
166
        lines = external_udiff_lines(['boo\n']*10000,
 
167
                                     ['goo\n']*10000,
174
168
                                     use_stringio=True)
175
169
        self.check_patch(lines)
176
170
 
177
171
    def test_external_diff_binary_lang_c(self):
178
172
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
179
173
            self.overrideEnv(lang, 'C')
180
 
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
 
174
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
181
175
        # Older versions of diffutils say "Binary files", newer
182
176
        # versions just say "Files".
183
 
        self.assertContainsRe(
184
 
            lines[0], b'(Binary f|F)iles old and new differ\n')
185
 
        self.assertEqual(lines[1:], [b'\n'])
 
177
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
 
178
        self.assertEqual(lines[1:], ['\n'])
186
179
 
187
180
    def test_no_external_diff(self):
188
181
        """Check that NoDiff is raised when diff is not available"""
190
183
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
191
184
        self.overrideEnv('PATH', '')
192
185
        self.assertRaises(errors.NoDiff, diff.external_diff,
193
 
                          b'old', [b'boo\n'], b'new', [b'goo\n'],
 
186
                          'old', ['boo\n'], 'new', ['goo\n'],
194
187
                          BytesIO(), diff_opts=['-u'])
195
188
 
196
189
    def test_internal_diff_default(self):
197
190
        # Default internal diff encoding is utf8
198
191
        output = BytesIO()
199
 
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
200
 
                           u'new_\xe5', [b'new_text\n'], output)
 
192
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
193
                           u'new_\xe5', ['new_text\n'], output)
201
194
        lines = output.getvalue().splitlines(True)
202
195
        self.check_patch(lines)
203
 
        self.assertEqual([b'--- old_\xc2\xb5\n',
204
 
                          b'+++ new_\xc3\xa5\n',
205
 
                          b'@@ -1,1 +1,1 @@\n',
206
 
                          b'-old_text\n',
207
 
                          b'+new_text\n',
208
 
                          b'\n',
209
 
                          ], lines)
 
196
        self.assertEqual(['--- old_\xc2\xb5\n',
 
197
                           '+++ new_\xc3\xa5\n',
 
198
                           '@@ -1,1 +1,1 @@\n',
 
199
                           '-old_text\n',
 
200
                           '+new_text\n',
 
201
                           '\n',
 
202
                          ]
 
203
                          , lines)
210
204
 
211
205
    def test_internal_diff_utf8(self):
212
206
        output = BytesIO()
213
 
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
214
 
                           u'new_\xe5', [b'new_text\n'], output,
 
207
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
208
                           u'new_\xe5', ['new_text\n'], output,
215
209
                           path_encoding='utf8')
216
210
        lines = output.getvalue().splitlines(True)
217
211
        self.check_patch(lines)
218
 
        self.assertEqual([b'--- old_\xc2\xb5\n',
219
 
                          b'+++ new_\xc3\xa5\n',
220
 
                          b'@@ -1,1 +1,1 @@\n',
221
 
                          b'-old_text\n',
222
 
                          b'+new_text\n',
223
 
                          b'\n',
224
 
                          ], lines)
 
212
        self.assertEqual(['--- old_\xc2\xb5\n',
 
213
                           '+++ new_\xc3\xa5\n',
 
214
                           '@@ -1,1 +1,1 @@\n',
 
215
                           '-old_text\n',
 
216
                           '+new_text\n',
 
217
                           '\n',
 
218
                          ]
 
219
                          , lines)
225
220
 
226
221
    def test_internal_diff_iso_8859_1(self):
227
222
        output = BytesIO()
228
 
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
229
 
                           u'new_\xe5', [b'new_text\n'], output,
 
223
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
224
                           u'new_\xe5', ['new_text\n'], output,
230
225
                           path_encoding='iso-8859-1')
231
226
        lines = output.getvalue().splitlines(True)
232
227
        self.check_patch(lines)
233
 
        self.assertEqual([b'--- old_\xb5\n',
234
 
                          b'+++ new_\xe5\n',
235
 
                          b'@@ -1,1 +1,1 @@\n',
236
 
                          b'-old_text\n',
237
 
                          b'+new_text\n',
238
 
                          b'\n',
239
 
                          ], lines)
 
228
        self.assertEqual(['--- old_\xb5\n',
 
229
                           '+++ new_\xe5\n',
 
230
                           '@@ -1,1 +1,1 @@\n',
 
231
                           '-old_text\n',
 
232
                           '+new_text\n',
 
233
                           '\n',
 
234
                          ]
 
235
                          , lines)
240
236
 
241
237
    def test_internal_diff_no_content(self):
242
238
        output = BytesIO()
243
239
        diff.internal_diff(u'old', [], u'new', [], output)
244
 
        self.assertEqual(b'', output.getvalue())
 
240
        self.assertEqual('', output.getvalue())
245
241
 
246
242
    def test_internal_diff_no_changes(self):
247
243
        output = BytesIO()
248
 
        diff.internal_diff(u'old', [b'text\n', b'contents\n'],
249
 
                           u'new', [b'text\n', b'contents\n'],
 
244
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
245
                           u'new', ['text\n', 'contents\n'],
250
246
                           output)
251
 
        self.assertEqual(b'', output.getvalue())
 
247
        self.assertEqual('', output.getvalue())
252
248
 
253
249
    def test_internal_diff_returns_bytes(self):
254
250
        output = StubO()
255
 
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
256
 
                           u'new_\xe5', [b'new_text\n'], output)
 
251
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
252
                            u'new_\xe5', ['new_text\n'], output)
257
253
        output.check_types(self, bytes)
258
254
 
259
255
    def test_internal_diff_default_context(self):
260
256
        output = BytesIO()
261
 
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
262
 
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
263
 
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
264
 
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output)
 
257
        diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
 
258
                           'same_text\n', 'same_text\n', 'old_text\n'],
 
259
                           'new', ['same_text\n', 'same_text\n', 'same_text\n',
 
260
                           'same_text\n', 'same_text\n', 'new_text\n'], output)
265
261
        lines = output.getvalue().splitlines(True)
266
262
        self.check_patch(lines)
267
 
        self.assertEqual([b'--- old\n',
268
 
                          b'+++ new\n',
269
 
                          b'@@ -3,4 +3,4 @@\n',
270
 
                          b' same_text\n',
271
 
                          b' same_text\n',
272
 
                          b' same_text\n',
273
 
                          b'-old_text\n',
274
 
                          b'+new_text\n',
275
 
                          b'\n',
276
 
                          ], lines)
 
263
        self.assertEqual(['--- old\n',
 
264
                           '+++ new\n',
 
265
                           '@@ -3,4 +3,4 @@\n',
 
266
                           ' same_text\n',
 
267
                           ' same_text\n',
 
268
                           ' same_text\n',
 
269
                           '-old_text\n',
 
270
                           '+new_text\n',
 
271
                           '\n',
 
272
                          ]
 
273
                          , lines)
277
274
 
278
275
    def test_internal_diff_no_context(self):
279
276
        output = BytesIO()
280
 
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
281
 
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
282
 
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
283
 
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output,
 
277
        diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
 
278
                           'same_text\n', 'same_text\n', 'old_text\n'],
 
279
                           'new', ['same_text\n', 'same_text\n', 'same_text\n',
 
280
                           'same_text\n', 'same_text\n', 'new_text\n'], output,
284
281
                           context_lines=0)
285
282
        lines = output.getvalue().splitlines(True)
286
283
        self.check_patch(lines)
287
 
        self.assertEqual([b'--- old\n',
288
 
                          b'+++ new\n',
289
 
                          b'@@ -6,1 +6,1 @@\n',
290
 
                          b'-old_text\n',
291
 
                          b'+new_text\n',
292
 
                          b'\n',
293
 
                          ], lines)
 
284
        self.assertEqual(['--- old\n',
 
285
                           '+++ new\n',
 
286
                           '@@ -6,1 +6,1 @@\n',
 
287
                           '-old_text\n',
 
288
                           '+new_text\n',
 
289
                           '\n',
 
290
                          ]
 
291
                          , lines)
294
292
 
295
293
    def test_internal_diff_more_context(self):
296
294
        output = BytesIO()
297
 
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
298
 
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
299
 
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
300
 
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output,
 
295
        diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
 
296
                           'same_text\n', 'same_text\n', 'old_text\n'],
 
297
                           'new', ['same_text\n', 'same_text\n', 'same_text\n',
 
298
                           'same_text\n', 'same_text\n', 'new_text\n'], output,
301
299
                           context_lines=4)
302
300
        lines = output.getvalue().splitlines(True)
303
301
        self.check_patch(lines)
304
 
        self.assertEqual([b'--- old\n',
305
 
                          b'+++ new\n',
306
 
                          b'@@ -2,5 +2,5 @@\n',
307
 
                          b' same_text\n',
308
 
                          b' same_text\n',
309
 
                          b' same_text\n',
310
 
                          b' same_text\n',
311
 
                          b'-old_text\n',
312
 
                          b'+new_text\n',
313
 
                          b'\n',
314
 
                          ], lines)
 
302
        self.assertEqual(['--- old\n',
 
303
                           '+++ new\n',
 
304
                           '@@ -2,5 +2,5 @@\n',
 
305
                           ' same_text\n',
 
306
                           ' same_text\n',
 
307
                           ' same_text\n',
 
308
                           ' same_text\n',
 
309
                           '-old_text\n',
 
310
                           '+new_text\n',
 
311
                           '\n',
 
312
                          ]
 
313
                          , lines)
 
314
 
 
315
 
 
316
 
315
317
 
316
318
 
317
319
class TestDiffFiles(tests.TestCaseInTempDir):
321
323
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
322
324
            self.overrideEnv(lang, 'C')
323
325
        # Make sure external_diff doesn't fail in the current LANG
324
 
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
 
326
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
325
327
 
326
328
        cmd = ['diff', '-u', '--binary', 'old', 'new']
327
 
        with open('old', 'wb') as f:
328
 
            f.write(b'\x00foobar\n')
329
 
        with open('new', 'wb') as f:
330
 
            f.write(b'foo\x00bar\n')
 
329
        with open('old', 'wb') as f: f.write('\x00foobar\n')
 
330
        with open('new', 'wb') as f: f.write('foo\x00bar\n')
331
331
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
332
 
                                stdin=subprocess.PIPE)
 
332
                                     stdin=subprocess.PIPE)
333
333
        out, err = pipe.communicate()
334
334
        # We should output whatever diff tells us, plus a trailing newline
335
 
        self.assertEqual(out.splitlines(True) + [b'\n'], lines)
 
335
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
336
336
 
337
337
 
338
338
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
342
342
    else:
343
343
        extra_trees = ()
344
344
    diff.show_diff_trees(tree1, tree2, output,
345
 
                         specific_files=specific_files,
346
 
                         extra_trees=extra_trees, old_label='old/',
347
 
                         new_label='new/')
 
345
        specific_files=specific_files,
 
346
        extra_trees=extra_trees, old_label='old/',
 
347
        new_label='new/')
348
348
    return output.getvalue()
349
349
 
350
350
 
361
361
        self.wt.add(['file1', 'file2'])
362
362
        self.wt.commit(
363
363
            message='Revision 1',
364
 
            timestamp=1143849600,  # 2006-04-01 00:00:00 UTC
 
364
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
365
365
            timezone=0,
366
366
            rev_id=b'rev-1')
367
367
        self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
368
368
        self.wt.commit(
369
369
            message='Revision 2',
370
 
            timestamp=1143936000,  # 2006-04-02 00:00:00 UTC
 
370
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
371
371
            timezone=28800,
372
372
            rev_id=b'rev-2')
373
373
        self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
374
374
        self.wt.commit(
375
375
            message='Revision 3',
376
 
            timestamp=1144022400,  # 2006-04-03 00:00:00 UTC
 
376
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
377
377
            timezone=-3600,
378
378
            rev_id=b'rev-3')
379
379
        self.wt.remove(['file2'])
380
380
        self.wt.commit(
381
381
            message='Revision 4',
382
 
            timestamp=1144108800,  # 2006-04-04 00:00:00 UTC
 
382
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
383
383
            timezone=0,
384
384
            rev_id=b'rev-4')
385
385
        self.build_tree_contents([
386
386
            ('file1', b'file1 contents in working tree\n')
387
387
            ])
388
388
        # set the date stamps for files in the working tree to known values
389
 
        os.utime('file1', (1144195200, 1144195200))  # 2006-04-05 00:00:00 UTC
 
389
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
390
390
 
391
391
    def test_diff_rev_tree_working_tree(self):
392
392
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
393
393
        # note that the date for old/file1 is from rev 2 rather than from
394
394
        # the basis revision (rev 4)
395
 
        self.assertEqualDiff(output, b'''\
 
395
        self.assertEqualDiff(output, '''\
396
396
=== modified file 'file1'
397
397
--- old/file1\t2006-04-02 00:00:00 +0000
398
398
+++ new/file1\t2006-04-05 00:00:00 +0000
403
403
''')
404
404
 
405
405
    def test_diff_rev_tree_rev_tree(self):
406
 
        tree1 = self.b.repository.revision_tree(b'rev-2')
407
 
        tree2 = self.b.repository.revision_tree(b'rev-3')
 
406
        tree1 = self.b.repository.revision_tree('rev-2')
 
407
        tree2 = self.b.repository.revision_tree('rev-3')
408
408
        output = get_diff_as_string(tree1, tree2)
409
 
        self.assertEqualDiff(output, b'''\
 
409
        self.assertEqualDiff(output, '''\
410
410
=== modified file 'file2'
411
411
--- old/file2\t2006-04-01 00:00:00 +0000
412
412
+++ new/file2\t2006-04-03 00:00:00 +0000
418
418
 
419
419
    def test_diff_add_files(self):
420
420
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
421
 
        tree2 = self.b.repository.revision_tree(b'rev-1')
 
421
        tree2 = self.b.repository.revision_tree('rev-1')
422
422
        output = get_diff_as_string(tree1, tree2)
423
423
        # the files have the epoch time stamp for the tree in which
424
424
        # they don't exist.
425
 
        self.assertEqualDiff(output, b'''\
 
425
        self.assertEqualDiff(output, '''\
426
426
=== added file 'file1'
427
427
--- old/file1\t1970-01-01 00:00:00 +0000
428
428
+++ new/file1\t2006-04-01 00:00:00 +0000
438
438
''')
439
439
 
440
440
    def test_diff_remove_files(self):
441
 
        tree1 = self.b.repository.revision_tree(b'rev-3')
442
 
        tree2 = self.b.repository.revision_tree(b'rev-4')
 
441
        tree1 = self.b.repository.revision_tree('rev-3')
 
442
        tree2 = self.b.repository.revision_tree('rev-4')
443
443
        output = get_diff_as_string(tree1, tree2)
444
444
        # the file has the epoch time stamp for the tree in which
445
445
        # it doesn't exist.
446
 
        self.assertEqualDiff(output, b'''\
 
446
        self.assertEqualDiff(output, '''\
447
447
=== removed file 'file2'
448
448
--- old/file2\t2006-04-03 00:00:00 +0000
449
449
+++ new/file2\t1970-01-01 00:00:00 +0000
455
455
    def test_show_diff_specified(self):
456
456
        """A working tree filename can be used to identify a file"""
457
457
        self.wt.rename_one('file1', 'file1b')
458
 
        old_tree = self.b.repository.revision_tree(b'rev-1')
459
 
        new_tree = self.b.repository.revision_tree(b'rev-4')
 
458
        old_tree = self.b.repository.revision_tree('rev-1')
 
459
        new_tree = self.b.repository.revision_tree('rev-4')
460
460
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
461
 
                                 working_tree=self.wt)
462
 
        self.assertContainsRe(out, b'file1\t')
 
461
                            working_tree=self.wt)
 
462
        self.assertContainsRe(out, 'file1\t')
463
463
 
464
464
    def test_recursive_diff(self):
465
465
        """Children of directories are matched"""
467
467
        os.mkdir('dir2')
468
468
        self.wt.add(['dir1', 'dir2'])
469
469
        self.wt.rename_one('file1', 'dir1/file1')
470
 
        old_tree = self.b.repository.revision_tree(b'rev-1')
471
 
        new_tree = self.b.repository.revision_tree(b'rev-4')
 
470
        old_tree = self.b.repository.revision_tree('rev-1')
 
471
        new_tree = self.b.repository.revision_tree('rev-4')
472
472
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
473
 
                                 working_tree=self.wt)
474
 
        self.assertContainsRe(out, b'file1\t')
 
473
                            working_tree=self.wt)
 
474
        self.assertContainsRe(out, 'file1\t')
475
475
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
476
 
                                 working_tree=self.wt)
477
 
        self.assertNotContainsRe(out, b'file1\t')
 
476
                            working_tree=self.wt)
 
477
        self.assertNotContainsRe(out, 'file1\t')
478
478
 
479
479
 
480
480
class TestShowDiffTrees(tests.TestCaseWithTransport):
489
489
 
490
490
        self.build_tree_contents([('tree/file', b'new contents\n')])
491
491
        d = get_diff_as_string(tree.basis_tree(), tree)
492
 
        self.assertContainsRe(d, b"=== modified file 'file'\n")
493
 
        self.assertContainsRe(d, b'--- old/file\t')
494
 
        self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
495
 
        self.assertContainsRe(d, b'-contents\n'
496
 
                                 b'\\+new contents\n')
 
492
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
493
        self.assertContainsRe(d, '--- old/file\t')
 
494
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
495
        self.assertContainsRe(d, '-contents\n'
 
496
                                 '\\+new contents\n')
497
497
 
498
498
    def test_modified_file_in_renamed_dir(self):
499
499
        """Test when a file is modified in a renamed directory."""
506
506
        tree.rename_one('dir', 'other')
507
507
        self.build_tree_contents([('tree/other/file', b'new contents\n')])
508
508
        d = get_diff_as_string(tree.basis_tree(), tree)
509
 
        self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
510
 
        self.assertContainsRe(d, b"=== modified file 'other/file'\n")
 
509
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
510
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
511
511
        # XXX: This is technically incorrect, because it used to be at another
512
512
        # location. What to do?
513
 
        self.assertContainsRe(d, b'--- old/dir/file\t')
514
 
        self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
515
 
        self.assertContainsRe(d, b'-contents\n'
516
 
                                 b'\\+new contents\n')
 
513
        self.assertContainsRe(d, '--- old/dir/file\t')
 
514
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
515
        self.assertContainsRe(d, '-contents\n'
 
516
                                 '\\+new contents\n')
517
517
 
518
518
    def test_renamed_directory(self):
519
519
        """Test when only a directory is only renamed."""
527
527
        d = get_diff_as_string(tree.basis_tree(), tree)
528
528
        # Renaming a directory should be a single "you renamed this dir" even
529
529
        # when there are files inside.
530
 
        self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
 
530
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
531
531
 
532
532
    def test_renamed_file(self):
533
533
        """Test when a file is only renamed."""
538
538
 
539
539
        tree.rename_one('file', 'newname')
540
540
        d = get_diff_as_string(tree.basis_tree(), tree)
541
 
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
 
541
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
542
542
        # We shouldn't have a --- or +++ line, because there is no content
543
543
        # change
544
 
        self.assertNotContainsRe(d, b'---')
 
544
        self.assertNotContainsRe(d, '---')
545
545
 
546
546
    def test_renamed_and_modified_file(self):
547
547
        """Test when a file is only renamed."""
553
553
        tree.rename_one('file', 'newname')
554
554
        self.build_tree_contents([('tree/newname', b'new contents\n')])
555
555
        d = get_diff_as_string(tree.basis_tree(), tree)
556
 
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
557
 
        self.assertContainsRe(d, b'--- old/file\t')
558
 
        self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
559
 
        self.assertContainsRe(d, b'-contents\n'
560
 
                                 b'\\+new contents\n')
 
556
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
557
        self.assertContainsRe(d, '--- old/file\t')
 
558
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
559
        self.assertContainsRe(d, '-contents\n'
 
560
                                 '\\+new contents\n')
 
561
 
561
562
 
562
563
    def test_internal_diff_exec_property(self):
563
564
        tree = self.make_branch_and_tree('tree')
564
565
 
565
 
        tt = tree.get_transform()
566
 
        tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
567
 
        tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
568
 
        tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
569
 
        tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
570
 
        tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
571
 
        tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
 
566
        tt = transform.TreeTransform(tree)
 
567
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
 
568
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
 
569
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
 
570
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
 
571
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
 
572
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
572
573
        tt.apply()
573
574
        tree.commit('one', rev_id=b'rev-1')
574
575
 
575
 
        tt = tree.get_transform()
576
 
        tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
577
 
        tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
578
 
        tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
579
 
        tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
 
576
        tt = transform.TreeTransform(tree)
 
577
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
 
578
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
 
579
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
 
580
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
580
581
        tt.apply()
581
582
        tree.rename_one('c', 'new-c')
582
583
        tree.rename_one('d', 'new-d')
583
584
 
584
585
        d = get_diff_as_string(tree.basis_tree(), tree)
585
586
 
586
 
        self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
587
 
                                 br".*\+x to -x.*\)")
588
 
        self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
589
 
                                 br".*-x to \+x.*\)")
590
 
        self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
591
 
                                 br".*\+x to -x.*\)")
592
 
        self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
593
 
                                 br".*-x to \+x.*\)")
594
 
        self.assertNotContainsRe(d, br"file 'e'")
595
 
        self.assertNotContainsRe(d, br"file 'f'")
 
587
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
588
                                 r".*\+x to -x.*\)")
 
589
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
590
                                 r".*-x to \+x.*\)")
 
591
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
592
                                 r".*\+x to -x.*\)")
 
593
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
594
                                 r".*-x to \+x.*\)")
 
595
        self.assertNotContainsRe(d, r"file 'e'")
 
596
        self.assertNotContainsRe(d, r"file 'f'")
596
597
 
597
598
    def test_binary_unicode_filenames(self):
598
599
        """Test that contents of files are *not* encoded in UTF-8 when there
605
606
        alpha, omega = u'\u03b1', u'\u03c9'
606
607
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
607
608
        self.build_tree_contents(
608
 
            [('tree/' + alpha, b'\0'),
 
609
            [('tree/' + alpha, chr(0)),
609
610
             ('tree/' + omega,
610
 
              (b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
 
611
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
611
612
        tree.add([alpha], [b'file-id'])
612
613
        tree.add([omega], [b'file-id-2'])
613
614
        diff_content = StubO()
614
615
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
615
616
        diff_content.check_types(self, bytes)
616
617
        d = b''.join(diff_content.write_record)
617
 
        self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
618
 
        self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
 
618
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
 
619
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
619
620
                              % (alpha_utf8, alpha_utf8))
620
 
        self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
621
 
        self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
622
 
        self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
 
621
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
 
622
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
 
623
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
623
624
 
624
625
    def test_unicode_filename(self):
625
626
        """Test when the filename are unicode."""
629
630
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
630
631
 
631
632
        tree = self.make_branch_and_tree('tree')
632
 
        self.build_tree_contents([('tree/ren_' + alpha, b'contents\n')])
633
 
        tree.add(['ren_' + alpha], [b'file-id-2'])
634
 
        self.build_tree_contents([('tree/del_' + alpha, b'contents\n')])
635
 
        tree.add(['del_' + alpha], [b'file-id-3'])
636
 
        self.build_tree_contents([('tree/mod_' + alpha, b'contents\n')])
637
 
        tree.add(['mod_' + alpha], [b'file-id-4'])
 
633
        self.build_tree_contents([('tree/ren_'+alpha, b'contents\n')])
 
634
        tree.add(['ren_'+alpha], [b'file-id-2'])
 
635
        self.build_tree_contents([('tree/del_'+alpha, b'contents\n')])
 
636
        tree.add(['del_'+alpha], [b'file-id-3'])
 
637
        self.build_tree_contents([('tree/mod_'+alpha, b'contents\n')])
 
638
        tree.add(['mod_'+alpha], [b'file-id-4'])
638
639
 
639
640
        tree.commit('one', rev_id=b'rev-1')
640
641
 
641
 
        tree.rename_one('ren_' + alpha, 'ren_' + omega)
642
 
        tree.remove('del_' + alpha)
643
 
        self.build_tree_contents([('tree/add_' + alpha, b'contents\n')])
644
 
        tree.add(['add_' + alpha], [b'file-id'])
645
 
        self.build_tree_contents([('tree/mod_' + alpha, b'contents_mod\n')])
 
642
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
 
643
        tree.remove('del_'+alpha)
 
644
        self.build_tree_contents([('tree/add_'+alpha, b'contents\n')])
 
645
        tree.add(['add_'+alpha], [b'file-id'])
 
646
        self.build_tree_contents([('tree/mod_'+alpha, b'contents_mod\n')])
646
647
 
647
648
        d = get_diff_as_string(tree.basis_tree(), tree)
648
649
        self.assertContainsRe(d,
649
 
                              b"=== renamed file 'ren_%s' => 'ren_%s'\n" % (autf8, outf8))
650
 
        self.assertContainsRe(d, b"=== added file 'add_%s'" % autf8)
651
 
        self.assertContainsRe(d, b"=== modified file 'mod_%s'" % autf8)
652
 
        self.assertContainsRe(d, b"=== removed file 'del_%s'" % autf8)
 
650
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
 
651
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
 
652
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
 
653
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
653
654
 
654
655
    def test_unicode_filename_path_encoding(self):
655
656
        """Test for bug #382699: unicode filenames on Windows should be shown
672
673
 
673
674
        sio = BytesIO()
674
675
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
675
 
                             path_encoding='cp1251')
 
676
            path_encoding='cp1251')
676
677
 
677
678
        output = subst_dates(sio.getvalue())
678
 
        shouldbe = (b'''\
 
679
        shouldbe = ('''\
679
680
=== added directory '%(directory)s'
680
681
=== added file '%(test_txt)s'
681
682
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
689
690
@@ -0,0 +1,1 @@
690
691
+foo
691
692
 
692
 
''' % {b'directory': _russian_test.encode('cp1251'),
693
 
            b'test_txt': test_txt.encode('cp1251'),
694
 
       })
 
693
''' % {'directory': _russian_test.encode('cp1251'),
 
694
       'test_txt': test_txt.encode('cp1251'),
 
695
      })
695
696
        self.assertEqualDiff(output, shouldbe)
696
697
 
697
698
 
698
699
class DiffWasIs(diff.DiffPath):
699
700
 
700
 
    def diff(self, old_path, new_path, old_kind, new_kind):
701
 
        self.to_file.write(b'was: ')
 
701
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
 
702
        self.to_file.write('was: ')
702
703
        self.to_file.write(self.old_tree.get_file(old_path).read())
703
 
        self.to_file.write(b'is: ')
 
704
        self.to_file.write('is: ')
704
705
        self.to_file.write(self.new_tree.get_file(new_path).read())
705
706
 
706
707
 
726
727
        self.new_tree.add('newdir')
727
728
        self.new_tree.add('newdir/newfile', b'file-id')
728
729
        differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
729
 
        differ.diff_text('olddir/oldfile', None, 'old label', 'new label')
 
730
        differ.diff_text('olddir/oldfile', None, 'old label',
 
731
                         'new label', b'file-id', None)
730
732
        self.assertEqual(
731
 
            b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
 
733
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
732
734
            differ.to_file.getvalue())
733
735
        differ.to_file.seek(0)
734
736
        differ.diff_text(None, 'newdir/newfile',
735
 
                         'old label', 'new label')
 
737
                         'old label', 'new label', None, b'file-id')
736
738
        self.assertEqual(
737
 
            b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
 
739
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
738
740
            differ.to_file.getvalue())
739
741
        differ.to_file.seek(0)
740
742
        differ.diff_text('olddir/oldfile', 'newdir/newfile',
741
 
                         'old label', 'new label')
 
743
                         'old label', 'new label', b'file-id', b'file-id')
742
744
        self.assertEqual(
743
 
            b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
 
745
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
744
746
            differ.to_file.getvalue())
745
747
 
746
748
    def test_diff_deletion(self):
750
752
        self.new_tree.add('file', b'file-id')
751
753
        os.unlink('new-tree/file')
752
754
        self.differ.show_diff(None)
753
 
        self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
 
755
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
754
756
 
755
757
    def test_diff_creation(self):
756
758
        self.build_tree_contents([('old-tree/file', b'contents'),
759
761
        self.new_tree.add('file', b'file-id')
760
762
        os.unlink('old-tree/file')
761
763
        self.differ.show_diff(None)
762
 
        self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
 
764
        self.assertContainsRe(self.differ.to_file.getvalue(), r'\+contents')
763
765
 
764
766
    def test_diff_symlink(self):
765
767
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
766
768
        differ.diff_symlink('old target', None)
767
 
        self.assertEqual(b"=== target was 'old target'\n",
 
769
        self.assertEqual("=== target was 'old target'\n",
768
770
                         differ.to_file.getvalue())
769
771
 
770
772
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
771
773
        differ.diff_symlink(None, 'new target')
772
 
        self.assertEqual(b"=== target is 'new target'\n",
 
774
        self.assertEqual("=== target is 'new target'\n",
773
775
                         differ.to_file.getvalue())
774
776
 
775
777
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
776
778
        differ.diff_symlink('old target', 'new target')
777
 
        self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
 
779
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
778
780
                         differ.to_file.getvalue())
779
781
 
780
782
    def test_diff(self):
786
788
                                  ('new-tree/newdir/newfile', b'new\n')])
787
789
        self.new_tree.add('newdir')
788
790
        self.new_tree.add('newdir/newfile', b'file-id')
789
 
        self.differ.diff('olddir/oldfile', 'newdir/newfile')
 
791
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
790
792
        self.assertContainsRe(
791
793
            self.differ.to_file.getvalue(),
792
 
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
793
 
            br' \@\@\n-old\n\+new\n\n')
 
794
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
795
            r' \@\@\n-old\n\+new\n\n')
794
796
 
795
797
    def test_diff_kind_change(self):
796
798
        self.requireFeature(features.SymlinkFeature)
802
804
        os.symlink('new', 'new-tree/newdir/newfile')
803
805
        self.new_tree.add('newdir')
804
806
        self.new_tree.add('newdir/newfile', b'file-id')
805
 
        self.differ.diff('olddir/oldfile', 'newdir/newfile')
 
807
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
806
808
        self.assertContainsRe(
807
809
            self.differ.to_file.getvalue(),
808
 
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
809
 
            br' \@\@\n-old\n\n')
 
810
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
 
811
            r' \@\@\n-old\n\n')
810
812
        self.assertContainsRe(self.differ.to_file.getvalue(),
811
 
                              b"=== target is 'new'\n")
 
813
                              "=== target is u'new'\n")
812
814
 
813
815
    def test_diff_directory(self):
814
816
        self.build_tree(['new-tree/new-dir/'])
815
817
        self.new_tree.add('new-dir', b'new-dir-id')
816
 
        self.differ.diff(None, 'new-dir')
817
 
        self.assertEqual(self.differ.to_file.getvalue(), b'')
 
818
        self.differ.diff(b'new-dir-id', None, 'new-dir')
 
819
        self.assertEqual(self.differ.to_file.getvalue(), '')
818
820
 
819
821
    def create_old_new(self):
820
822
        self.build_tree_contents([('old-tree/olddir/',),
829
831
    def test_register_diff(self):
830
832
        self.create_old_new()
831
833
        old_diff_factories = diff.DiffTree.diff_factories
832
 
        diff.DiffTree.diff_factories = old_diff_factories[:]
 
834
        diff.DiffTree.diff_factories=old_diff_factories[:]
833
835
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
834
836
        try:
835
837
            differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
836
838
        finally:
837
839
            diff.DiffTree.diff_factories = old_diff_factories
838
 
        differ.diff('olddir/oldfile', 'newdir/newfile')
 
840
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
839
841
        self.assertNotContainsRe(
840
842
            differ.to_file.getvalue(),
841
 
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
842
 
            br' \@\@\n-old\n\+new\n\n')
 
843
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
844
            r' \@\@\n-old\n\+new\n\n')
843
845
        self.assertContainsRe(differ.to_file.getvalue(),
844
 
                              b'was: old\nis: new\n')
 
846
                              'was: old\nis: new\n')
845
847
 
846
848
    def test_extra_factories(self):
847
849
        self.create_old_new()
848
850
        differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
849
851
                               extra_factories=[DiffWasIs.from_diff_tree])
850
 
        differ.diff('olddir/oldfile', 'newdir/newfile')
 
852
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
851
853
        self.assertNotContainsRe(
852
854
            differ.to_file.getvalue(),
853
 
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
854
 
            br' \@\@\n-old\n\+new\n\n')
 
855
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
856
            r' \@\@\n-old\n\+new\n\n')
855
857
        self.assertContainsRe(differ.to_file.getvalue(),
856
 
                              b'was: old\nis: new\n')
 
858
                              'was: old\nis: new\n')
857
859
 
858
860
    def test_alphabetical_order(self):
859
861
        self.build_tree(['new-tree/a-file'])
862
864
        self.old_tree.add('b-file')
863
865
        self.differ.show_diff(None)
864
866
        self.assertContainsRe(self.differ.to_file.getvalue(),
865
 
                              b'.*a-file(.|\n)*b-file')
 
867
            '.*a-file(.|\n)*b-file')
 
868
 
 
869
 
 
870
class TestPatienceDiffLib(tests.TestCase):
 
871
 
 
872
    def setUp(self):
 
873
        super(TestPatienceDiffLib, self).setUp()
 
874
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
875
        self._recurse_matches = _patiencediff_py.recurse_matches_py
 
876
        self._PatienceSequenceMatcher = \
 
877
            _patiencediff_py.PatienceSequenceMatcher_py
 
878
 
 
879
    def test_diff_unicode_string(self):
 
880
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
 
881
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
 
882
        sm = self._PatienceSequenceMatcher(None, a, b)
 
883
        mb = sm.get_matching_blocks()
 
884
        self.assertEqual(35, len(mb))
 
885
 
 
886
    def test_unique_lcs(self):
 
887
        unique_lcs = self._unique_lcs
 
888
        self.assertEqual(unique_lcs('', ''), [])
 
889
        self.assertEqual(unique_lcs('', 'a'), [])
 
890
        self.assertEqual(unique_lcs('a', ''), [])
 
891
        self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
 
892
        self.assertEqual(unique_lcs('a', 'b'), [])
 
893
        self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
 
894
        self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
 
895
        self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
 
896
        self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
 
897
                                                         (3, 3), (4, 4)])
 
898
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
 
899
 
 
900
    def test_recurse_matches(self):
 
901
        def test_one(a, b, matches):
 
902
            test_matches = []
 
903
            self._recurse_matches(
 
904
                a, b, 0, 0, len(a), len(b), test_matches, 10)
 
905
            self.assertEqual(test_matches, matches)
 
906
 
 
907
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
 
908
                 [(0, 0), (2, 2), (4, 4)])
 
909
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
 
910
                 [(0, 0), (2, 1), (4, 2)])
 
911
        # Even though 'bc' is not unique globally, and is surrounded by
 
912
        # non-matching lines, we should still match, because they are locally
 
913
        # unique
 
914
        test_one('abcdbce', 'afbcgdbce', [(0, 0), (1, 2), (2, 3), (3, 5),
 
915
                                          (4, 6), (5, 7), (6, 8)])
 
916
 
 
917
        # recurse_matches doesn't match non-unique
 
918
        # lines surrounded by bogus text.
 
919
        # The update has been done in patiencediff.SequenceMatcher instead
 
920
 
 
921
        # This is what it could be
 
922
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
 
923
 
 
924
        # This is what it currently gives:
 
925
        test_one('aBccDe', 'abccde', [(0, 0), (5, 5)])
 
926
 
 
927
    def assertDiffBlocks(self, a, b, expected_blocks):
 
928
        """Check that the sequence matcher returns the correct blocks.
 
929
 
 
930
        :param a: A sequence to match
 
931
        :param b: Another sequence to match
 
932
        :param expected_blocks: The expected output, not including the final
 
933
            matching block (len(a), len(b), 0)
 
934
        """
 
935
        matcher = self._PatienceSequenceMatcher(None, a, b)
 
936
        blocks = matcher.get_matching_blocks()
 
937
        last = blocks.pop()
 
938
        self.assertEqual((len(a), len(b), 0), last)
 
939
        self.assertEqual(expected_blocks, blocks)
 
940
 
 
941
    def test_matching_blocks(self):
 
942
        # Some basic matching tests
 
943
        self.assertDiffBlocks('', '', [])
 
944
        self.assertDiffBlocks([], [], [])
 
945
        self.assertDiffBlocks('abc', '', [])
 
946
        self.assertDiffBlocks('', 'abc', [])
 
947
        self.assertDiffBlocks('abcd', 'abcd', [(0, 0, 4)])
 
948
        self.assertDiffBlocks('abcd', 'abce', [(0, 0, 3)])
 
949
        self.assertDiffBlocks('eabc', 'abce', [(1, 0, 3)])
 
950
        self.assertDiffBlocks('eabce', 'abce', [(1, 0, 4)])
 
951
        self.assertDiffBlocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
952
        self.assertDiffBlocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
953
        self.assertDiffBlocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
954
        # This may check too much, but it checks to see that
 
955
        # a copied block stays attached to the previous section,
 
956
        # not the later one.
 
957
        # difflib would tend to grab the trailing longest match
 
958
        # which would make the diff not look right
 
959
        self.assertDiffBlocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
960
                              [(0, 0, 6), (6, 11, 10)])
 
961
 
 
962
        # make sure it supports passing in lists
 
963
        self.assertDiffBlocks(
 
964
                   ['hello there\n',
 
965
                    'world\n',
 
966
                    'how are you today?\n'],
 
967
                   ['hello there\n',
 
968
                    'how are you today?\n'],
 
969
                [(0, 0, 1), (2, 1, 1)])
 
970
 
 
971
        # non unique lines surrounded by non-matching lines
 
972
        # won't be found
 
973
        self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
 
974
 
 
975
        # But they only need to be locally unique
 
976
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
 
977
 
 
978
        # non unique blocks won't be matched
 
979
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
 
980
 
 
981
        # but locally unique ones will
 
982
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
 
983
                                              (5, 4, 1), (7, 5, 2), (10, 8, 1)])
 
984
 
 
985
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
 
986
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
 
987
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
 
988
 
 
989
    def test_matching_blocks_tuples(self):
 
990
        # Some basic matching tests
 
991
        self.assertDiffBlocks([], [], [])
 
992
        self.assertDiffBlocks([('a',), ('b',), ('c,')], [], [])
 
993
        self.assertDiffBlocks([], [('a',), ('b',), ('c,')], [])
 
994
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
 
995
                              [('a',), ('b',), ('c,')],
 
996
                              [(0, 0, 3)])
 
997
        self.assertDiffBlocks([('a',), ('b',), ('c,')],
 
998
                              [('a',), ('b',), ('d,')],
 
999
                              [(0, 0, 2)])
 
1000
        self.assertDiffBlocks([('d',), ('b',), ('c,')],
 
1001
                              [('a',), ('b',), ('c,')],
 
1002
                              [(1, 1, 2)])
 
1003
        self.assertDiffBlocks([('d',), ('a',), ('b',), ('c,')],
 
1004
                              [('a',), ('b',), ('c,')],
 
1005
                              [(1, 0, 3)])
 
1006
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
 
1007
                              [('a', 'b'), ('c', 'X'), ('e', 'f')],
 
1008
                              [(0, 0, 1), (2, 2, 1)])
 
1009
        self.assertDiffBlocks([('a', 'b'), ('c', 'd'), ('e', 'f')],
 
1010
                              [('a', 'b'), ('c', 'dX'), ('e', 'f')],
 
1011
                              [(0, 0, 1), (2, 2, 1)])
 
1012
 
 
1013
    def test_opcodes(self):
 
1014
        def chk_ops(a, b, expected_codes):
 
1015
            s = self._PatienceSequenceMatcher(None, a, b)
 
1016
            self.assertEqual(expected_codes, s.get_opcodes())
 
1017
 
 
1018
        chk_ops('', '', [])
 
1019
        chk_ops([], [], [])
 
1020
        chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
 
1021
        chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
 
1022
        chk_ops('abcd', 'abcd', [('equal',    0, 4, 0, 4)])
 
1023
        chk_ops('abcd', 'abce', [('equal',   0, 3, 0, 3),
 
1024
                                 ('replace', 3, 4, 3, 4)
 
1025
                                ])
 
1026
        chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
 
1027
                                 ('equal',  1, 4, 0, 3),
 
1028
                                 ('insert', 4, 4, 3, 4)
 
1029
                                ])
 
1030
        chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
 
1031
                                  ('equal',  1, 5, 0, 4)
 
1032
                                 ])
 
1033
        chk_ops('abcde', 'abXde', [('equal',   0, 2, 0, 2),
 
1034
                                   ('replace', 2, 3, 2, 3),
 
1035
                                   ('equal',   3, 5, 3, 5)
 
1036
                                  ])
 
1037
        chk_ops('abcde', 'abXYZde', [('equal',   0, 2, 0, 2),
 
1038
                                     ('replace', 2, 3, 2, 5),
 
1039
                                     ('equal',   3, 5, 5, 7)
 
1040
                                    ])
 
1041
        chk_ops('abde', 'abXYZde', [('equal',  0, 2, 0, 2),
 
1042
                                    ('insert', 2, 2, 2, 5),
 
1043
                                    ('equal',  2, 4, 5, 7)
 
1044
                                   ])
 
1045
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1046
                [('equal',  0, 6,  0, 6),
 
1047
                 ('insert', 6, 6,  6, 11),
 
1048
                 ('equal',  6, 16, 11, 21)
 
1049
                ])
 
1050
        chk_ops(
 
1051
                [ 'hello there\n'
 
1052
                , 'world\n'
 
1053
                , 'how are you today?\n'],
 
1054
                [ 'hello there\n'
 
1055
                , 'how are you today?\n'],
 
1056
                [('equal',  0, 1, 0, 1),
 
1057
                 ('delete', 1, 2, 1, 1),
 
1058
                 ('equal',  2, 3, 1, 2),
 
1059
                ])
 
1060
        chk_ops('aBccDe', 'abccde',
 
1061
                [('equal',   0, 1, 0, 1),
 
1062
                 ('replace', 1, 5, 1, 5),
 
1063
                 ('equal',   5, 6, 5, 6),
 
1064
                ])
 
1065
        chk_ops('aBcDec', 'abcdec',
 
1066
                [('equal',   0, 1, 0, 1),
 
1067
                 ('replace', 1, 2, 1, 2),
 
1068
                 ('equal',   2, 3, 2, 3),
 
1069
                 ('replace', 3, 4, 3, 4),
 
1070
                 ('equal',   4, 6, 4, 6),
 
1071
                ])
 
1072
        chk_ops('aBcdEcdFg', 'abcdecdfg',
 
1073
                [('equal',   0, 1, 0, 1),
 
1074
                 ('replace', 1, 8, 1, 8),
 
1075
                 ('equal',   8, 9, 8, 9)
 
1076
                ])
 
1077
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
 
1078
                [('equal',   0, 1, 0, 1),
 
1079
                 ('replace', 1, 2, 1, 2),
 
1080
                 ('equal',   2, 4, 2, 4),
 
1081
                 ('delete', 4, 5, 4, 4),
 
1082
                 ('equal',   5, 6, 4, 5),
 
1083
                 ('delete', 6, 7, 5, 5),
 
1084
                 ('equal',   7, 9, 5, 7),
 
1085
                 ('replace', 9, 10, 7, 8),
 
1086
                 ('equal',   10, 11, 8, 9)
 
1087
                ])
 
1088
 
 
1089
    def test_grouped_opcodes(self):
 
1090
        def chk_ops(a, b, expected_codes, n=3):
 
1091
            s = self._PatienceSequenceMatcher(None, a, b)
 
1092
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
 
1093
 
 
1094
        chk_ops('', '', [])
 
1095
        chk_ops([], [], [])
 
1096
        chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
 
1097
        chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
 
1098
        chk_ops('abcd', 'abcd', [])
 
1099
        chk_ops('abcd', 'abce', [[('equal',   0, 3, 0, 3),
 
1100
                                  ('replace', 3, 4, 3, 4)
 
1101
                                 ]])
 
1102
        chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
 
1103
                                 ('equal',  1, 4, 0, 3),
 
1104
                                 ('insert', 4, 4, 3, 4)
 
1105
                                ]])
 
1106
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1107
                [[('equal',  3, 6, 3, 6),
 
1108
                  ('insert', 6, 6, 6, 11),
 
1109
                  ('equal',  6, 9, 11, 14)
 
1110
                  ]])
 
1111
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
1112
                [[('equal',  2, 6, 2, 6),
 
1113
                  ('insert', 6, 6, 6, 11),
 
1114
                  ('equal',  6, 10, 11, 15)
 
1115
                  ]], 4)
 
1116
        chk_ops('Xabcdef', 'abcdef',
 
1117
                [[('delete', 0, 1, 0, 0),
 
1118
                  ('equal',  1, 4, 0, 3)
 
1119
                  ]])
 
1120
        chk_ops('abcdef', 'abcdefX',
 
1121
                [[('equal',  3, 6, 3, 6),
 
1122
                  ('insert', 6, 6, 6, 7)
 
1123
                  ]])
 
1124
 
 
1125
 
 
1126
    def test_multiple_ranges(self):
 
1127
        # There was an earlier bug where we used a bad set of ranges,
 
1128
        # this triggers that specific bug, to make sure it doesn't regress
 
1129
        self.assertDiffBlocks('abcdefghijklmnop',
 
1130
                              'abcXghiYZQRSTUVWXYZijklmnop',
 
1131
                              [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
1132
 
 
1133
        self.assertDiffBlocks('ABCd efghIjk  L',
 
1134
                              'AxyzBCn mo pqrstuvwI1 2  L',
 
1135
                              [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1136
 
 
1137
        # These are rot13 code snippets.
 
1138
        self.assertDiffBlocks('''\
 
1139
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
 
1140
    """
 
1141
    gnxrf_netf = ['svyr*']
 
1142
    gnxrf_bcgvbaf = ['ab-erphefr']
 
1143
 
 
1144
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
 
1145
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
 
1146
        vs vf_dhvrg():
 
1147
            ercbegre = nqq_ercbegre_ahyy
 
1148
        ryfr:
 
1149
            ercbegre = nqq_ercbegre_cevag
 
1150
        fzneg_nqq(svyr_yvfg, abg ab_erphefr, ercbegre)
 
1151
 
 
1152
 
 
1153
pynff pzq_zxqve(Pbzznaq):
 
1154
'''.splitlines(True), '''\
 
1155
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
 
1156
 
 
1157
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
 
1158
    nqq gurz.
 
1159
    """
 
1160
    gnxrf_netf = ['svyr*']
 
1161
    gnxrf_bcgvbaf = ['ab-erphefr', 'qel-eha']
 
1162
 
 
1163
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr, qel_eha=Snyfr):
 
1164
        vzcbeg omeyvo.nqq
 
1165
 
 
1166
        vs qel_eha:
 
1167
            vs vf_dhvrg():
 
1168
                # Guvf vf cbvagyrff, ohg V'q engure abg envfr na reebe
 
1169
                npgvba = omeyvo.nqq.nqq_npgvba_ahyy
 
1170
            ryfr:
 
1171
  npgvba = omeyvo.nqq.nqq_npgvba_cevag
 
1172
        ryvs vf_dhvrg():
 
1173
            npgvba = omeyvo.nqq.nqq_npgvba_nqq
 
1174
        ryfr:
 
1175
       npgvba = omeyvo.nqq.nqq_npgvba_nqq_naq_cevag
 
1176
 
 
1177
        omeyvo.nqq.fzneg_nqq(svyr_yvfg, abg ab_erphefr, npgvba)
 
1178
 
 
1179
 
 
1180
pynff pzq_zxqve(Pbzznaq):
 
1181
'''.splitlines(True)
 
1182
, [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1183
 
 
1184
    def test_patience_unified_diff(self):
 
1185
        txt_a = ['hello there\n',
 
1186
                 'world\n',
 
1187
                 'how are you today?\n']
 
1188
        txt_b = ['hello there\n',
 
1189
                 'how are you today?\n']
 
1190
        unified_diff = patiencediff.unified_diff
 
1191
        psm = self._PatienceSequenceMatcher
 
1192
        self.assertEqual(['--- \n',
 
1193
                           '+++ \n',
 
1194
                           '@@ -1,3 +1,2 @@\n',
 
1195
                           ' hello there\n',
 
1196
                           '-world\n',
 
1197
                           ' how are you today?\n'
 
1198
                          ]
 
1199
                          , list(unified_diff(txt_a, txt_b,
 
1200
                                 sequencematcher=psm)))
 
1201
        txt_a = [x+'\n' for x in 'abcdefghijklmnop']
 
1202
        txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
 
1203
        # This is the result with LongestCommonSubstring matching
 
1204
        self.assertEqual(['--- \n',
 
1205
                           '+++ \n',
 
1206
                           '@@ -1,6 +1,11 @@\n',
 
1207
                           ' a\n',
 
1208
                           ' b\n',
 
1209
                           ' c\n',
 
1210
                           '+d\n',
 
1211
                           '+e\n',
 
1212
                           '+f\n',
 
1213
                           '+x\n',
 
1214
                           '+y\n',
 
1215
                           ' d\n',
 
1216
                           ' e\n',
 
1217
                           ' f\n']
 
1218
                          , list(unified_diff(txt_a, txt_b)))
 
1219
        # And the patience diff
 
1220
        self.assertEqual(['--- \n',
 
1221
                           '+++ \n',
 
1222
                           '@@ -4,6 +4,11 @@\n',
 
1223
                           ' d\n',
 
1224
                           ' e\n',
 
1225
                           ' f\n',
 
1226
                           '+x\n',
 
1227
                           '+y\n',
 
1228
                           '+d\n',
 
1229
                           '+e\n',
 
1230
                           '+f\n',
 
1231
                           ' g\n',
 
1232
                           ' h\n',
 
1233
                           ' i\n',
 
1234
                          ]
 
1235
                          , list(unified_diff(txt_a, txt_b,
 
1236
                                 sequencematcher=psm)))
 
1237
 
 
1238
    def test_patience_unified_diff_with_dates(self):
 
1239
        txt_a = ['hello there\n',
 
1240
                 'world\n',
 
1241
                 'how are you today?\n']
 
1242
        txt_b = ['hello there\n',
 
1243
                 'how are you today?\n']
 
1244
        unified_diff = patiencediff.unified_diff
 
1245
        psm = self._PatienceSequenceMatcher
 
1246
        self.assertEqual(['--- a\t2008-08-08\n',
 
1247
                           '+++ b\t2008-09-09\n',
 
1248
                           '@@ -1,3 +1,2 @@\n',
 
1249
                           ' hello there\n',
 
1250
                           '-world\n',
 
1251
                           ' how are you today?\n'
 
1252
                          ]
 
1253
                          , list(unified_diff(txt_a, txt_b,
 
1254
                                 fromfile='a', tofile='b',
 
1255
                                 fromfiledate='2008-08-08',
 
1256
                                 tofiledate='2008-09-09',
 
1257
                                 sequencematcher=psm)))
 
1258
 
 
1259
 
 
1260
class TestPatienceDiffLib_c(TestPatienceDiffLib):
 
1261
 
 
1262
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1263
 
 
1264
    def setUp(self):
 
1265
        super(TestPatienceDiffLib_c, self).setUp()
 
1266
        from breezy import _patiencediff_c
 
1267
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1268
        self._recurse_matches = _patiencediff_c.recurse_matches_c
 
1269
        self._PatienceSequenceMatcher = \
 
1270
            _patiencediff_c.PatienceSequenceMatcher_c
 
1271
 
 
1272
    def test_unhashable(self):
 
1273
        """We should get a proper exception here."""
 
1274
        # We need to be able to hash items in the sequence, lists are
 
1275
        # unhashable, and thus cannot be diffed
 
1276
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1277
                                         None, [[]], [])
 
1278
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1279
                                         None, ['valid', []], [])
 
1280
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1281
                                         None, ['valid'], [[]])
 
1282
        e = self.assertRaises(TypeError, self._PatienceSequenceMatcher,
 
1283
                                         None, ['valid'], ['valid', []])
 
1284
 
 
1285
 
 
1286
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
 
1287
 
 
1288
    def setUp(self):
 
1289
        super(TestPatienceDiffLibFiles, self).setUp()
 
1290
        self._PatienceSequenceMatcher = \
 
1291
            _patiencediff_py.PatienceSequenceMatcher_py
 
1292
 
 
1293
    def test_patience_unified_diff_files(self):
 
1294
        txt_a = ['hello there\n',
 
1295
                 'world\n',
 
1296
                 'how are you today?\n']
 
1297
        txt_b = ['hello there\n',
 
1298
                 'how are you today?\n']
 
1299
        with open('a1', 'wb') as f: f.writelines(txt_a)
 
1300
        with open('b1', 'wb') as f: f.writelines(txt_b)
 
1301
 
 
1302
        unified_diff_files = patiencediff.unified_diff_files
 
1303
        psm = self._PatienceSequenceMatcher
 
1304
        self.assertEqual(['--- a1\n',
 
1305
                           '+++ b1\n',
 
1306
                           '@@ -1,3 +1,2 @@\n',
 
1307
                           ' hello there\n',
 
1308
                           '-world\n',
 
1309
                           ' how are you today?\n',
 
1310
                          ]
 
1311
                          , list(unified_diff_files('a1', 'b1',
 
1312
                                 sequencematcher=psm)))
 
1313
 
 
1314
        txt_a = [x+'\n' for x in 'abcdefghijklmnop']
 
1315
        txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
 
1316
        with open('a2', 'wb') as f: f.writelines(txt_a)
 
1317
        with open('b2', 'wb') as f: f.writelines(txt_b)
 
1318
 
 
1319
        # This is the result with LongestCommonSubstring matching
 
1320
        self.assertEqual(['--- a2\n',
 
1321
                           '+++ b2\n',
 
1322
                           '@@ -1,6 +1,11 @@\n',
 
1323
                           ' a\n',
 
1324
                           ' b\n',
 
1325
                           ' c\n',
 
1326
                           '+d\n',
 
1327
                           '+e\n',
 
1328
                           '+f\n',
 
1329
                           '+x\n',
 
1330
                           '+y\n',
 
1331
                           ' d\n',
 
1332
                           ' e\n',
 
1333
                           ' f\n']
 
1334
                          , list(unified_diff_files('a2', 'b2')))
 
1335
 
 
1336
        # And the patience diff
 
1337
        self.assertEqual(['--- a2\n',
 
1338
                          '+++ b2\n',
 
1339
                          '@@ -4,6 +4,11 @@\n',
 
1340
                          ' d\n',
 
1341
                          ' e\n',
 
1342
                          ' f\n',
 
1343
                          '+x\n',
 
1344
                          '+y\n',
 
1345
                          '+d\n',
 
1346
                          '+e\n',
 
1347
                          '+f\n',
 
1348
                          ' g\n',
 
1349
                          ' h\n',
 
1350
                          ' i\n'],
 
1351
                         list(unified_diff_files('a2', 'b2',
 
1352
                                                 sequencematcher=psm)))
 
1353
 
 
1354
 
 
1355
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
 
1356
 
 
1357
    _test_needs_features = [features.compiled_patiencediff_feature]
 
1358
 
 
1359
    def setUp(self):
 
1360
        super(TestPatienceDiffLibFiles_c, self).setUp()
 
1361
        from breezy import _patiencediff_c
 
1362
        self._PatienceSequenceMatcher = \
 
1363
            _patiencediff_c.PatienceSequenceMatcher_c
 
1364
 
 
1365
 
 
1366
class TestUsingCompiledIfAvailable(tests.TestCase):
 
1367
 
 
1368
    def test_PatienceSequenceMatcher(self):
 
1369
        if features.compiled_patiencediff_feature.available():
 
1370
            from breezy._patiencediff_c import PatienceSequenceMatcher_c
 
1371
            self.assertIs(PatienceSequenceMatcher_c,
 
1372
                          patiencediff.PatienceSequenceMatcher)
 
1373
        else:
 
1374
            from breezy._patiencediff_py import PatienceSequenceMatcher_py
 
1375
            self.assertIs(PatienceSequenceMatcher_py,
 
1376
                          patiencediff.PatienceSequenceMatcher)
 
1377
 
 
1378
    def test_unique_lcs(self):
 
1379
        if features.compiled_patiencediff_feature.available():
 
1380
            from breezy._patiencediff_c import unique_lcs_c
 
1381
            self.assertIs(unique_lcs_c,
 
1382
                          patiencediff.unique_lcs)
 
1383
        else:
 
1384
            from breezy._patiencediff_py import unique_lcs_py
 
1385
            self.assertIs(unique_lcs_py,
 
1386
                          patiencediff.unique_lcs)
 
1387
 
 
1388
    def test_recurse_matches(self):
 
1389
        if features.compiled_patiencediff_feature.available():
 
1390
            from breezy._patiencediff_c import recurse_matches_c
 
1391
            self.assertIs(recurse_matches_c,
 
1392
                          patiencediff.recurse_matches)
 
1393
        else:
 
1394
            from breezy._patiencediff_py import recurse_matches_py
 
1395
            self.assertIs(recurse_matches_py,
 
1396
                          patiencediff.recurse_matches)
866
1397
 
867
1398
 
868
1399
class TestDiffFromTool(tests.TestCaseWithTransport):
869
1400
 
870
1401
    def test_from_string(self):
871
 
        diff_obj = diff.DiffFromTool.from_string(
872
 
            ['diff', '{old_path}', '{new_path}'],
873
 
            None, None, None)
874
 
        self.addCleanup(diff_obj.finish)
875
 
        self.assertEqual(['diff', '{old_path}', '{new_path}'],
876
 
                         diff_obj.command_template)
877
 
 
878
 
    def test_from_string_no_paths(self):
879
 
        diff_obj = diff.DiffFromTool.from_string(
880
 
            ['diff', "-u5"], None, None, None)
881
 
        self.addCleanup(diff_obj.finish)
882
 
        self.assertEqual(['diff', '-u5'],
883
 
                         diff_obj.command_template)
884
 
        self.assertEqual(['diff', '-u5', 'old-path', 'new-path'],
885
 
                         diff_obj._get_command('old-path', 'new-path'))
 
1402
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
 
1403
        self.addCleanup(diff_obj.finish)
 
1404
        self.assertEqual(['diff', '@old_path', '@new_path'],
 
1405
            diff_obj.command_template)
886
1406
 
887
1407
    def test_from_string_u5(self):
888
 
        diff_obj = diff.DiffFromTool.from_string(
889
 
            ['diff', "-u 5", '{old_path}', '{new_path}'], None, None, None)
 
1408
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1409
                                                 None, None, None)
890
1410
        self.addCleanup(diff_obj.finish)
891
 
        self.assertEqual(['diff', '-u 5', '{old_path}', '{new_path}'],
 
1411
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
892
1412
                         diff_obj.command_template)
893
1413
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
894
1414
                         diff_obj._get_command('old-path', 'new-path'))
895
1415
 
896
1416
    def test_from_string_path_with_backslashes(self):
897
1417
        self.requireFeature(features.backslashdir_feature)
898
 
        tool = ['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}']
 
1418
        tool = 'C:\\Tools\\Diff.exe'
899
1419
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
900
1420
        self.addCleanup(diff_obj.finish)
901
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}'],
 
1421
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
902
1422
                         diff_obj.command_template)
903
1423
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
904
1424
                         diff_obj._get_command('old-path', 'new-path'))
905
1425
 
906
1426
    def test_execute(self):
907
1427
        output = BytesIO()
908
 
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
909
 
                                      'print("{old_path} {new_path}")'],
 
1428
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1429
                                      'print "@old_path @new_path"'],
910
1430
                                     None, None, output)
911
1431
        self.addCleanup(diff_obj.finish)
912
1432
        diff_obj._execute('old', 'new')
913
 
        self.assertEqual(output.getvalue().rstrip(), b'old new')
 
1433
        self.assertEqual(output.getvalue().rstrip(), 'old new')
914
1434
 
915
1435
    def test_execute_missing(self):
916
1436
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
933
1453
        basis_tree = tree.basis_tree()
934
1454
        basis_tree.lock_read()
935
1455
        self.addCleanup(basis_tree.unlock)
936
 
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
937
 
                                      'print "{old_path} {new_path}"'],
 
1456
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1457
                                      'print "@old_path @new_path"'],
938
1458
                                     basis_tree, tree, output)
939
1459
        diff_obj._prepare_files('file', 'file', file_id=b'file-id')
940
1460
        # The old content should be readonly
969
1489
        self.addCleanup(old_tree.unlock)
970
1490
        tree.lock_read()
971
1491
        self.addCleanup(tree.unlock)
972
 
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
973
 
                                      'print "{old_path} {new_path}"'],
 
1492
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1493
                                      'print "@old_path @new_path"'],
974
1494
                                     old_tree, tree, output)
975
1495
        self.addCleanup(diff_obj.finish)
976
1496
        self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
977
1497
        old_path, new_path = diff_obj._prepare_files(
978
 
            'oldname', 'newname')
 
1498
                'oldname', 'newname', file_id='file-id')
979
1499
        self.assertContainsRe(old_path, 'old/oldname$')
980
1500
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
981
1501
        self.assertContainsRe(new_path, 'tree/newname$')
982
 
        self.assertFileEqual(b'oldcontent', old_path)
983
 
        self.assertFileEqual(b'newcontent', new_path)
 
1502
        self.assertFileEqual('oldcontent', old_path)
 
1503
        self.assertFileEqual('newcontent', new_path)
984
1504
        if osutils.host_os_dereferences_symlinks():
985
1505
            self.assertTrue(os.path.samefile('tree/newname', new_path))
986
1506
        # make sure we can create files with the same parent directories
987
 
        diff_obj._prepare_files('oldname2', 'newname2')
 
1507
        diff_obj._prepare_files('oldname2', 'newname2', file_id='file2-id')
988
1508
 
989
1509
 
990
1510
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
992
1512
    def test_encodable_filename(self):
993
1513
        # Just checks file path for external diff tool.
994
1514
        # We cannot change CPython's internal encoding used by os.exec*.
995
 
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
 
1515
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
996
1516
                                    None, None, None)
997
1517
        for _, scenario in EncodingAdapter.encoding_scenarios:
998
1518
            encoding = scenario['encoding']
1007
1527
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1008
1528
 
1009
1529
    def test_unencodable_filename(self):
1010
 
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
 
1530
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1011
1531
                                    None, None, None)
1012
1532
        for _, scenario in EncodingAdapter.encoding_scenarios:
1013
1533
            encoding = scenario['encoding']
1031
1551
 
1032
1552
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1033
1553
        """Call get_trees_and_branches_to_diff_locked."""
1034
 
        exit_stack = cleanup.ExitStack()
1035
 
        self.addCleanup(exit_stack.close)
1036
1554
        return diff.get_trees_and_branches_to_diff_locked(
1037
 
            path_list, revision_specs, old_url, new_url, exit_stack)
 
1555
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1038
1556
 
1039
1557
    def test_basic(self):
1040
1558
        tree = self.make_branch_and_tree('tree')
1058
1576
        tree.add('file', b'file-id')
1059
1577
        tree.commit('old tree', timestamp=0, rev_id=b"old-id")
1060
1578
        self.build_tree_contents([('tree/file', b'newcontent')])
1061
 
        tree.commit('new tree', timestamp=0, rev_id=b"new-id")
 
1579
        tree.commit('new tree', timestamp=0, rev_id="new-id")
1062
1580
 
1063
1581
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1064
1582
                     revisionspec.RevisionSpec.from_string('2')]
1068
1586
            ['tree'], revisions, None, None)
1069
1587
 
1070
1588
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1071
 
        self.assertEqual(b"old-id", old_tree.get_revision_id())
 
1589
        self.assertEqual("old-id", old_tree.get_revision_id())
1072
1590
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1073
 
        self.assertEqual(b"new-id", new_tree.get_revision_id())
 
1591
        self.assertEqual("new-id", new_tree.get_revision_id())
1074
1592
        self.assertEqual(tree.branch.base, old_branch.base)
1075
1593
        self.assertEqual(tree.branch.base, new_branch.base)
1076
1594
        self.assertIs(None, specific_files)