/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: 2020-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
import contextlib
 
18
from io import BytesIO
17
19
import os
 
20
import re
18
21
import subprocess
 
22
import sys
19
23
import tempfile
20
24
 
21
25
from .. import (
22
26
    diff,
23
27
    errors,
24
28
    osutils,
25
 
    patiencediff,
26
 
    _patiencediff_py,
27
29
    revision as _mod_revision,
28
30
    revisionspec,
29
31
    revisiontree,
30
32
    tests,
31
 
    transform,
32
 
    )
33
 
from ..sixish import (
34
 
    BytesIO,
35
33
    )
36
34
from ..tests import (
37
35
    features,
38
36
    EncodingAdapter,
39
37
    )
40
 
from ..tests.blackbox.test_diff import subst_dates
41
38
from ..tests.scenarios import load_tests_apply_scenarios
42
39
 
43
40
 
44
41
load_tests = load_tests_apply_scenarios
45
42
 
46
43
 
 
44
def subst_dates(string):
 
45
    """Replace date strings with constant values."""
 
46
    return re.sub(br'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
 
47
                  b'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
 
48
 
 
49
 
47
50
def udiff_lines(old, new, allow_binary=False):
48
51
    output = BytesIO()
49
52
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
97
100
class TestDiffOptionsScenarios(tests.TestCase):
98
101
 
99
102
    scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
100
 
    style = None # Set by load_tests_apply_scenarios from scenarios
 
103
    style = None  # Set by load_tests_apply_scenarios from scenarios
101
104
 
102
105
    def test_unified_not_added(self):
103
106
        # Verify that for all valid style options, '-u' is not
110
113
 
111
114
    def test_add_nl(self):
112
115
        """diff generates a valid diff for patches that add a newline"""
113
 
        lines = udiff_lines(['boo'], ['boo\n'])
 
116
        lines = udiff_lines([b'boo'], [b'boo\n'])
114
117
        self.check_patch(lines)
115
 
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
116
 
            ## "expected no-nl, got %r" % lines[4]
 
118
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
 
119
        ## "expected no-nl, got %r" % lines[4]
117
120
 
118
121
    def test_add_nl_2(self):
119
122
        """diff generates a valid diff for patches that change last line and
120
123
        add a newline.
121
124
        """
122
 
        lines = udiff_lines(['boo'], ['goo\n'])
 
125
        lines = udiff_lines([b'boo'], [b'goo\n'])
123
126
        self.check_patch(lines)
124
 
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
125
 
            ## "expected no-nl, got %r" % lines[4]
 
127
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
 
128
        ## "expected no-nl, got %r" % lines[4]
126
129
 
127
130
    def test_remove_nl(self):
128
131
        """diff generates a valid diff for patches that change last line and
129
132
        add a newline.
130
133
        """
131
 
        lines = udiff_lines(['boo\n'], ['boo'])
 
134
        lines = udiff_lines([b'boo\n'], [b'boo'])
132
135
        self.check_patch(lines)
133
 
        self.assertEqual(lines[5], '\\ No newline at end of file\n')
134
 
            ## "expected no-nl, got %r" % lines[5]
 
136
        self.assertEqual(lines[5], b'\\ No newline at end of file\n')
 
137
        ## "expected no-nl, got %r" % lines[5]
135
138
 
136
139
    def check_patch(self, lines):
137
140
        self.assertTrue(len(lines) > 1)
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)
 
141
        ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
 
142
        self.assertTrue(lines[0].startswith(b'---'))
 
143
        ## 'No orig line for patch:\n%s' % "".join(lines)
 
144
        self.assertTrue(lines[1].startswith(b'+++'))
 
145
        ## 'No mod line for patch:\n%s' % "".join(lines)
143
146
        self.assertTrue(len(lines) > 2)
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)
 
147
        ## "No hunks for patch:\n%s" % "".join(lines)
 
148
        self.assertTrue(lines[2].startswith(b'@@'))
 
149
        ## "No hunk header for patch:\n%s" % "".join(lines)
 
150
        self.assertTrue(b'@@' in lines[2][2:])
 
151
        ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
149
152
 
150
153
    def test_binary_lines(self):
151
154
        empty = []
152
 
        uni_lines = [1023 * 'a' + '\x00']
 
155
        uni_lines = [1023 * b'a' + b'\x00']
153
156
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
154
157
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
155
158
        udiff_lines(uni_lines, empty, allow_binary=True)
156
159
        udiff_lines(empty, uni_lines, allow_binary=True)
157
160
 
158
161
    def test_external_diff(self):
159
 
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
 
162
        lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
160
163
        self.check_patch(lines)
161
 
        self.assertEqual('\n', lines[-1])
 
164
        self.assertEqual(b'\n', lines[-1])
162
165
 
163
166
    def test_external_diff_no_fileno(self):
164
167
        # Make sure that we can handle not having a fileno, even
165
168
        # if the diff is large
166
 
        lines = external_udiff_lines(['boo\n']*10000,
167
 
                                     ['goo\n']*10000,
 
169
        lines = external_udiff_lines([b'boo\n'] * 10000,
 
170
                                     [b'goo\n'] * 10000,
168
171
                                     use_stringio=True)
169
172
        self.check_patch(lines)
170
173
 
171
174
    def test_external_diff_binary_lang_c(self):
172
175
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
173
176
            self.overrideEnv(lang, 'C')
174
 
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
177
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
175
178
        # Older versions of diffutils say "Binary files", newer
176
179
        # versions just say "Files".
177
 
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
178
 
        self.assertEqual(lines[1:], ['\n'])
 
180
        self.assertContainsRe(
 
181
            lines[0], b'(Binary f|F)iles old and new differ\n')
 
182
        self.assertEqual(lines[1:], [b'\n'])
179
183
 
180
184
    def test_no_external_diff(self):
181
185
        """Check that NoDiff is raised when diff is not available"""
183
187
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
184
188
        self.overrideEnv('PATH', '')
185
189
        self.assertRaises(errors.NoDiff, diff.external_diff,
186
 
                          'old', ['boo\n'], 'new', ['goo\n'],
 
190
                          b'old', [b'boo\n'], b'new', [b'goo\n'],
187
191
                          BytesIO(), diff_opts=['-u'])
188
192
 
189
193
    def test_internal_diff_default(self):
190
194
        # Default internal diff encoding is utf8
191
195
        output = BytesIO()
192
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
193
 
                           u'new_\xe5', ['new_text\n'], output)
 
196
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
197
                           u'new_\xe5', [b'new_text\n'], output)
194
198
        lines = output.getvalue().splitlines(True)
195
199
        self.check_patch(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)
 
200
        self.assertEqual([b'--- old_\xc2\xb5\n',
 
201
                          b'+++ new_\xc3\xa5\n',
 
202
                          b'@@ -1,1 +1,1 @@\n',
 
203
                          b'-old_text\n',
 
204
                          b'+new_text\n',
 
205
                          b'\n',
 
206
                          ], lines)
204
207
 
205
208
    def test_internal_diff_utf8(self):
206
209
        output = BytesIO()
207
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
208
 
                           u'new_\xe5', ['new_text\n'], output,
 
210
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
211
                           u'new_\xe5', [b'new_text\n'], output,
209
212
                           path_encoding='utf8')
210
213
        lines = output.getvalue().splitlines(True)
211
214
        self.check_patch(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)
 
215
        self.assertEqual([b'--- old_\xc2\xb5\n',
 
216
                          b'+++ new_\xc3\xa5\n',
 
217
                          b'@@ -1,1 +1,1 @@\n',
 
218
                          b'-old_text\n',
 
219
                          b'+new_text\n',
 
220
                          b'\n',
 
221
                          ], lines)
220
222
 
221
223
    def test_internal_diff_iso_8859_1(self):
222
224
        output = BytesIO()
223
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
224
 
                           u'new_\xe5', ['new_text\n'], output,
 
225
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
226
                           u'new_\xe5', [b'new_text\n'], output,
225
227
                           path_encoding='iso-8859-1')
226
228
        lines = output.getvalue().splitlines(True)
227
229
        self.check_patch(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)
 
230
        self.assertEqual([b'--- old_\xb5\n',
 
231
                          b'+++ new_\xe5\n',
 
232
                          b'@@ -1,1 +1,1 @@\n',
 
233
                          b'-old_text\n',
 
234
                          b'+new_text\n',
 
235
                          b'\n',
 
236
                          ], lines)
236
237
 
237
238
    def test_internal_diff_no_content(self):
238
239
        output = BytesIO()
239
240
        diff.internal_diff(u'old', [], u'new', [], output)
240
 
        self.assertEqual('', output.getvalue())
 
241
        self.assertEqual(b'', output.getvalue())
241
242
 
242
243
    def test_internal_diff_no_changes(self):
243
244
        output = BytesIO()
244
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
245
 
                           u'new', ['text\n', 'contents\n'],
 
245
        diff.internal_diff(u'old', [b'text\n', b'contents\n'],
 
246
                           u'new', [b'text\n', b'contents\n'],
246
247
                           output)
247
 
        self.assertEqual('', output.getvalue())
 
248
        self.assertEqual(b'', output.getvalue())
248
249
 
249
250
    def test_internal_diff_returns_bytes(self):
250
251
        output = StubO()
251
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
252
 
                            u'new_\xe5', ['new_text\n'], output)
 
252
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
253
                           u'new_\xe5', [b'new_text\n'], output)
253
254
        output.check_types(self, bytes)
254
255
 
255
256
    def test_internal_diff_default_context(self):
256
257
        output = BytesIO()
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)
 
258
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
259
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
 
260
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
261
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output)
261
262
        lines = output.getvalue().splitlines(True)
262
263
        self.check_patch(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)
 
264
        self.assertEqual([b'--- old\n',
 
265
                          b'+++ new\n',
 
266
                          b'@@ -3,4 +3,4 @@\n',
 
267
                          b' same_text\n',
 
268
                          b' same_text\n',
 
269
                          b' same_text\n',
 
270
                          b'-old_text\n',
 
271
                          b'+new_text\n',
 
272
                          b'\n',
 
273
                          ], lines)
274
274
 
275
275
    def test_internal_diff_no_context(self):
276
276
        output = BytesIO()
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,
 
277
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
278
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
 
279
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
280
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output,
281
281
                           context_lines=0)
282
282
        lines = output.getvalue().splitlines(True)
283
283
        self.check_patch(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)
 
284
        self.assertEqual([b'--- old\n',
 
285
                          b'+++ new\n',
 
286
                          b'@@ -6,1 +6,1 @@\n',
 
287
                          b'-old_text\n',
 
288
                          b'+new_text\n',
 
289
                          b'\n',
 
290
                          ], lines)
292
291
 
293
292
    def test_internal_diff_more_context(self):
294
293
        output = BytesIO()
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,
 
294
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
295
                                   b'same_text\n', b'same_text\n', b'old_text\n'],
 
296
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
297
                                   b'same_text\n', b'same_text\n', b'new_text\n'], output,
299
298
                           context_lines=4)
300
299
        lines = output.getvalue().splitlines(True)
301
300
        self.check_patch(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
 
 
 
301
        self.assertEqual([b'--- old\n',
 
302
                          b'+++ new\n',
 
303
                          b'@@ -2,5 +2,5 @@\n',
 
304
                          b' same_text\n',
 
305
                          b' same_text\n',
 
306
                          b' same_text\n',
 
307
                          b' same_text\n',
 
308
                          b'-old_text\n',
 
309
                          b'+new_text\n',
 
310
                          b'\n',
 
311
                          ], lines)
317
312
 
318
313
 
319
314
class TestDiffFiles(tests.TestCaseInTempDir):
323
318
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
324
319
            self.overrideEnv(lang, 'C')
325
320
        # Make sure external_diff doesn't fail in the current LANG
326
 
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
321
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
327
322
 
328
323
        cmd = ['diff', '-u', '--binary', 'old', 'new']
329
 
        with open('old', 'wb') as f: f.write('\x00foobar\n')
330
 
        with open('new', 'wb') as f: f.write('foo\x00bar\n')
 
324
        with open('old', 'wb') as f:
 
325
            f.write(b'\x00foobar\n')
 
326
        with open('new', 'wb') as f:
 
327
            f.write(b'foo\x00bar\n')
331
328
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
332
 
                                     stdin=subprocess.PIPE)
 
329
                                stdin=subprocess.PIPE)
333
330
        out, err = pipe.communicate()
334
331
        # We should output whatever diff tells us, plus a trailing newline
335
 
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
 
332
        self.assertEqual(out.splitlines(True) + [b'\n'], lines)
336
333
 
337
334
 
338
335
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
342
339
    else:
343
340
        extra_trees = ()
344
341
    diff.show_diff_trees(tree1, tree2, output,
345
 
        specific_files=specific_files,
346
 
        extra_trees=extra_trees, old_label='old/',
347
 
        new_label='new/')
 
342
                         specific_files=specific_files,
 
343
                         extra_trees=extra_trees, old_label='old/',
 
344
                         new_label='new/')
348
345
    return output.getvalue()
349
346
 
350
347
 
355
352
        self.wt = self.make_branch_and_tree('.')
356
353
        self.b = self.wt.branch
357
354
        self.build_tree_contents([
358
 
            ('file1', 'file1 contents at rev 1\n'),
359
 
            ('file2', 'file2 contents at rev 1\n')
 
355
            ('file1', b'file1 contents at rev 1\n'),
 
356
            ('file2', b'file2 contents at rev 1\n')
360
357
            ])
361
358
        self.wt.add(['file1', 'file2'])
362
359
        self.wt.commit(
363
360
            message='Revision 1',
364
 
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
 
361
            timestamp=1143849600,  # 2006-04-01 00:00:00 UTC
365
362
            timezone=0,
366
 
            rev_id='rev-1')
367
 
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
 
363
            rev_id=b'rev-1')
 
364
        self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
368
365
        self.wt.commit(
369
366
            message='Revision 2',
370
 
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
 
367
            timestamp=1143936000,  # 2006-04-02 00:00:00 UTC
371
368
            timezone=28800,
372
 
            rev_id='rev-2')
373
 
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
 
369
            rev_id=b'rev-2')
 
370
        self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
374
371
        self.wt.commit(
375
372
            message='Revision 3',
376
 
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
 
373
            timestamp=1144022400,  # 2006-04-03 00:00:00 UTC
377
374
            timezone=-3600,
378
 
            rev_id='rev-3')
 
375
            rev_id=b'rev-3')
379
376
        self.wt.remove(['file2'])
380
377
        self.wt.commit(
381
378
            message='Revision 4',
382
 
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
 
379
            timestamp=1144108800,  # 2006-04-04 00:00:00 UTC
383
380
            timezone=0,
384
 
            rev_id='rev-4')
 
381
            rev_id=b'rev-4')
385
382
        self.build_tree_contents([
386
 
            ('file1', 'file1 contents in working tree\n')
 
383
            ('file1', b'file1 contents in working tree\n')
387
384
            ])
388
385
        # 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
 
386
        os.utime('file1', (1144195200, 1144195200))  # 2006-04-05 00:00:00 UTC
390
387
 
391
388
    def test_diff_rev_tree_working_tree(self):
392
389
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
393
390
        # note that the date for old/file1 is from rev 2 rather than from
394
391
        # the basis revision (rev 4)
395
 
        self.assertEqualDiff(output, '''\
 
392
        self.assertEqualDiff(output, b'''\
396
393
=== modified file 'file1'
397
394
--- old/file1\t2006-04-02 00:00:00 +0000
398
395
+++ new/file1\t2006-04-05 00:00:00 +0000
403
400
''')
404
401
 
405
402
    def test_diff_rev_tree_rev_tree(self):
406
 
        tree1 = self.b.repository.revision_tree('rev-2')
407
 
        tree2 = self.b.repository.revision_tree('rev-3')
 
403
        tree1 = self.b.repository.revision_tree(b'rev-2')
 
404
        tree2 = self.b.repository.revision_tree(b'rev-3')
408
405
        output = get_diff_as_string(tree1, tree2)
409
 
        self.assertEqualDiff(output, '''\
 
406
        self.assertEqualDiff(output, b'''\
410
407
=== modified file 'file2'
411
408
--- old/file2\t2006-04-01 00:00:00 +0000
412
409
+++ new/file2\t2006-04-03 00:00:00 +0000
418
415
 
419
416
    def test_diff_add_files(self):
420
417
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
421
 
        tree2 = self.b.repository.revision_tree('rev-1')
 
418
        tree2 = self.b.repository.revision_tree(b'rev-1')
422
419
        output = get_diff_as_string(tree1, tree2)
423
420
        # the files have the epoch time stamp for the tree in which
424
421
        # they don't exist.
425
 
        self.assertEqualDiff(output, '''\
 
422
        self.assertEqualDiff(output, b'''\
426
423
=== added file 'file1'
427
424
--- old/file1\t1970-01-01 00:00:00 +0000
428
425
+++ new/file1\t2006-04-01 00:00:00 +0000
438
435
''')
439
436
 
440
437
    def test_diff_remove_files(self):
441
 
        tree1 = self.b.repository.revision_tree('rev-3')
442
 
        tree2 = self.b.repository.revision_tree('rev-4')
 
438
        tree1 = self.b.repository.revision_tree(b'rev-3')
 
439
        tree2 = self.b.repository.revision_tree(b'rev-4')
443
440
        output = get_diff_as_string(tree1, tree2)
444
441
        # the file has the epoch time stamp for the tree in which
445
442
        # it doesn't exist.
446
 
        self.assertEqualDiff(output, '''\
 
443
        self.assertEqualDiff(output, b'''\
447
444
=== removed file 'file2'
448
445
--- old/file2\t2006-04-03 00:00:00 +0000
449
446
+++ new/file2\t1970-01-01 00:00:00 +0000
455
452
    def test_show_diff_specified(self):
456
453
        """A working tree filename can be used to identify a file"""
457
454
        self.wt.rename_one('file1', 'file1b')
458
 
        old_tree = self.b.repository.revision_tree('rev-1')
459
 
        new_tree = self.b.repository.revision_tree('rev-4')
 
455
        old_tree = self.b.repository.revision_tree(b'rev-1')
 
456
        new_tree = self.b.repository.revision_tree(b'rev-4')
460
457
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
461
 
                            working_tree=self.wt)
462
 
        self.assertContainsRe(out, 'file1\t')
 
458
                                 working_tree=self.wt)
 
459
        self.assertContainsRe(out, b'file1\t')
463
460
 
464
461
    def test_recursive_diff(self):
465
462
        """Children of directories are matched"""
467
464
        os.mkdir('dir2')
468
465
        self.wt.add(['dir1', 'dir2'])
469
466
        self.wt.rename_one('file1', 'dir1/file1')
470
 
        old_tree = self.b.repository.revision_tree('rev-1')
471
 
        new_tree = self.b.repository.revision_tree('rev-4')
 
467
        old_tree = self.b.repository.revision_tree(b'rev-1')
 
468
        new_tree = self.b.repository.revision_tree(b'rev-4')
472
469
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
473
 
                            working_tree=self.wt)
474
 
        self.assertContainsRe(out, 'file1\t')
 
470
                                 working_tree=self.wt)
 
471
        self.assertContainsRe(out, b'file1\t')
475
472
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
476
 
                            working_tree=self.wt)
477
 
        self.assertNotContainsRe(out, 'file1\t')
 
473
                                 working_tree=self.wt)
 
474
        self.assertNotContainsRe(out, b'file1\t')
478
475
 
479
476
 
480
477
class TestShowDiffTrees(tests.TestCaseWithTransport):
483
480
    def test_modified_file(self):
484
481
        """Test when a file is modified."""
485
482
        tree = self.make_branch_and_tree('tree')
486
 
        self.build_tree_contents([('tree/file', 'contents\n')])
487
 
        tree.add(['file'], ['file-id'])
488
 
        tree.commit('one', rev_id='rev-1')
 
483
        self.build_tree_contents([('tree/file', b'contents\n')])
 
484
        tree.add(['file'], [b'file-id'])
 
485
        tree.commit('one', rev_id=b'rev-1')
489
486
 
490
 
        self.build_tree_contents([('tree/file', 'new contents\n')])
 
487
        self.build_tree_contents([('tree/file', b'new contents\n')])
491
488
        d = get_diff_as_string(tree.basis_tree(), tree)
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')
 
489
        self.assertContainsRe(d, b"=== modified file 'file'\n")
 
490
        self.assertContainsRe(d, b'--- old/file\t')
 
491
        self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
 
492
        self.assertContainsRe(d, b'-contents\n'
 
493
                                 b'\\+new contents\n')
497
494
 
498
495
    def test_modified_file_in_renamed_dir(self):
499
496
        """Test when a file is modified in a renamed directory."""
500
497
        tree = self.make_branch_and_tree('tree')
501
498
        self.build_tree(['tree/dir/'])
502
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
503
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
504
 
        tree.commit('one', rev_id='rev-1')
 
499
        self.build_tree_contents([('tree/dir/file', b'contents\n')])
 
500
        tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
 
501
        tree.commit('one', rev_id=b'rev-1')
505
502
 
506
503
        tree.rename_one('dir', 'other')
507
 
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
 
504
        self.build_tree_contents([('tree/other/file', b'new contents\n')])
508
505
        d = get_diff_as_string(tree.basis_tree(), tree)
509
 
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
510
 
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
 
506
        self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
 
507
        self.assertContainsRe(d, b"=== modified file 'other/file'\n")
511
508
        # XXX: This is technically incorrect, because it used to be at another
512
509
        # location. What to do?
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')
 
510
        self.assertContainsRe(d, b'--- old/dir/file\t')
 
511
        self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
 
512
        self.assertContainsRe(d, b'-contents\n'
 
513
                                 b'\\+new contents\n')
517
514
 
518
515
    def test_renamed_directory(self):
519
516
        """Test when only a directory is only renamed."""
520
517
        tree = self.make_branch_and_tree('tree')
521
518
        self.build_tree(['tree/dir/'])
522
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
523
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
524
 
        tree.commit('one', rev_id='rev-1')
 
519
        self.build_tree_contents([('tree/dir/file', b'contents\n')])
 
520
        tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
 
521
        tree.commit('one', rev_id=b'rev-1')
525
522
 
526
523
        tree.rename_one('dir', 'newdir')
527
524
        d = get_diff_as_string(tree.basis_tree(), tree)
528
525
        # Renaming a directory should be a single "you renamed this dir" even
529
526
        # when there are files inside.
530
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
527
        self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
531
528
 
532
529
    def test_renamed_file(self):
533
530
        """Test when a file is only renamed."""
534
531
        tree = self.make_branch_and_tree('tree')
535
 
        self.build_tree_contents([('tree/file', 'contents\n')])
536
 
        tree.add(['file'], ['file-id'])
537
 
        tree.commit('one', rev_id='rev-1')
 
532
        self.build_tree_contents([('tree/file', b'contents\n')])
 
533
        tree.add(['file'], [b'file-id'])
 
534
        tree.commit('one', rev_id=b'rev-1')
538
535
 
539
536
        tree.rename_one('file', 'newname')
540
537
        d = get_diff_as_string(tree.basis_tree(), tree)
541
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
538
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
542
539
        # We shouldn't have a --- or +++ line, because there is no content
543
540
        # change
544
 
        self.assertNotContainsRe(d, '---')
 
541
        self.assertNotContainsRe(d, b'---')
545
542
 
546
543
    def test_renamed_and_modified_file(self):
547
544
        """Test when a file is only renamed."""
548
545
        tree = self.make_branch_and_tree('tree')
549
 
        self.build_tree_contents([('tree/file', 'contents\n')])
550
 
        tree.add(['file'], ['file-id'])
551
 
        tree.commit('one', rev_id='rev-1')
 
546
        self.build_tree_contents([('tree/file', b'contents\n')])
 
547
        tree.add(['file'], [b'file-id'])
 
548
        tree.commit('one', rev_id=b'rev-1')
552
549
 
553
550
        tree.rename_one('file', 'newname')
554
 
        self.build_tree_contents([('tree/newname', 'new contents\n')])
 
551
        self.build_tree_contents([('tree/newname', b'new contents\n')])
555
552
        d = get_diff_as_string(tree.basis_tree(), tree)
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
 
 
 
553
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
 
554
        self.assertContainsRe(d, b'--- old/file\t')
 
555
        self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
 
556
        self.assertContainsRe(d, b'-contents\n'
 
557
                                 b'\\+new contents\n')
562
558
 
563
559
    def test_internal_diff_exec_property(self):
564
560
        tree = self.make_branch_and_tree('tree')
565
561
 
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)
 
562
        tt = tree.get_transform()
 
563
        tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
 
564
        tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
 
565
        tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
 
566
        tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
 
567
        tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
 
568
        tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
573
569
        tt.apply()
574
 
        tree.commit('one', rev_id='rev-1')
 
570
        tree.commit('one', rev_id=b'rev-1')
575
571
 
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'))
 
572
        tt = tree.get_transform()
 
573
        tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
 
574
        tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
 
575
        tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
 
576
        tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
581
577
        tt.apply()
582
578
        tree.rename_one('c', 'new-c')
583
579
        tree.rename_one('d', 'new-d')
584
580
 
585
581
        d = get_diff_as_string(tree.basis_tree(), tree)
586
582
 
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'")
 
583
        self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
 
584
                                 br".*\+x to -x.*\)")
 
585
        self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
 
586
                                 br".*-x to \+x.*\)")
 
587
        self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
 
588
                                 br".*\+x to -x.*\)")
 
589
        self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
 
590
                                 br".*-x to \+x.*\)")
 
591
        self.assertNotContainsRe(d, br"file 'e'")
 
592
        self.assertNotContainsRe(d, br"file 'f'")
597
593
 
598
594
    def test_binary_unicode_filenames(self):
599
595
        """Test that contents of files are *not* encoded in UTF-8 when there
606
602
        alpha, omega = u'\u03b1', u'\u03c9'
607
603
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
608
604
        self.build_tree_contents(
609
 
            [('tree/' + alpha, chr(0)),
 
605
            [('tree/' + alpha, b'\0'),
610
606
             ('tree/' + omega,
611
 
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
612
 
        tree.add([alpha], ['file-id'])
613
 
        tree.add([omega], ['file-id-2'])
 
607
              (b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
 
608
        tree.add([alpha], [b'file-id'])
 
609
        tree.add([omega], [b'file-id-2'])
614
610
        diff_content = StubO()
615
611
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
616
612
        diff_content.check_types(self, bytes)
617
613
        d = b''.join(diff_content.write_record)
618
 
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
619
 
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
614
        self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
 
615
        self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
620
616
                              % (alpha_utf8, alpha_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,))
 
617
        self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
 
618
        self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
 
619
        self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
624
620
 
625
621
    def test_unicode_filename(self):
626
622
        """Test when the filename are unicode."""
630
626
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
631
627
 
632
628
        tree = self.make_branch_and_tree('tree')
633
 
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
634
 
        tree.add(['ren_'+alpha], ['file-id-2'])
635
 
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
636
 
        tree.add(['del_'+alpha], ['file-id-3'])
637
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
638
 
        tree.add(['mod_'+alpha], ['file-id-4'])
639
 
 
640
 
        tree.commit('one', rev_id='rev-1')
641
 
 
642
 
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
643
 
        tree.remove('del_'+alpha)
644
 
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
645
 
        tree.add(['add_'+alpha], ['file-id'])
646
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
 
629
        self.build_tree_contents([('tree/ren_' + alpha, b'contents\n')])
 
630
        tree.add(['ren_' + alpha], [b'file-id-2'])
 
631
        self.build_tree_contents([('tree/del_' + alpha, b'contents\n')])
 
632
        tree.add(['del_' + alpha], [b'file-id-3'])
 
633
        self.build_tree_contents([('tree/mod_' + alpha, b'contents\n')])
 
634
        tree.add(['mod_' + alpha], [b'file-id-4'])
 
635
 
 
636
        tree.commit('one', rev_id=b'rev-1')
 
637
 
 
638
        tree.rename_one('ren_' + alpha, 'ren_' + omega)
 
639
        tree.remove('del_' + alpha)
 
640
        self.build_tree_contents([('tree/add_' + alpha, b'contents\n')])
 
641
        tree.add(['add_' + alpha], [b'file-id'])
 
642
        self.build_tree_contents([('tree/mod_' + alpha, b'contents_mod\n')])
647
643
 
648
644
        d = get_diff_as_string(tree.basis_tree(), tree)
649
645
        self.assertContainsRe(d,
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)
 
646
                              b"=== renamed file 'ren_%s' => 'ren_%s'\n" % (autf8, outf8))
 
647
        self.assertContainsRe(d, b"=== added file 'add_%s'" % autf8)
 
648
        self.assertContainsRe(d, b"=== modified file 'mod_%s'" % autf8)
 
649
        self.assertContainsRe(d, b"=== removed file 'del_%s'" % autf8)
654
650
 
655
651
    def test_unicode_filename_path_encoding(self):
656
652
        """Test for bug #382699: unicode filenames on Windows should be shown
665
661
 
666
662
        tree = self.make_branch_and_tree('.')
667
663
        self.build_tree_contents([
668
 
            (test_txt, 'foo\n'),
669
 
            (u1234, 'foo\n'),
 
664
            (test_txt, b'foo\n'),
 
665
            (u1234, b'foo\n'),
670
666
            (directory, None),
671
667
            ])
672
668
        tree.add([test_txt, u1234, directory])
673
669
 
674
670
        sio = BytesIO()
675
671
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
676
 
            path_encoding='cp1251')
 
672
                             path_encoding='cp1251')
677
673
 
678
674
        output = subst_dates(sio.getvalue())
679
 
        shouldbe = ('''\
 
675
        shouldbe = (b'''\
680
676
=== added directory '%(directory)s'
681
677
=== added file '%(test_txt)s'
682
678
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
690
686
@@ -0,0 +1,1 @@
691
687
+foo
692
688
 
693
 
''' % {'directory': _russian_test.encode('cp1251'),
694
 
       'test_txt': test_txt.encode('cp1251'),
695
 
      })
 
689
''' % {b'directory': _russian_test.encode('cp1251'),
 
690
            b'test_txt': test_txt.encode('cp1251'),
 
691
       })
696
692
        self.assertEqualDiff(output, shouldbe)
697
693
 
698
694
 
699
695
class DiffWasIs(diff.DiffPath):
700
696
 
701
 
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
702
 
        self.to_file.write('was: ')
 
697
    def diff(self, old_path, new_path, old_kind, new_kind):
 
698
        self.to_file.write(b'was: ')
703
699
        self.to_file.write(self.old_tree.get_file(old_path).read())
704
 
        self.to_file.write('is: ')
 
700
        self.to_file.write(b'is: ')
705
701
        self.to_file.write(self.new_tree.get_file(new_path).read())
706
702
 
707
703
 
719
715
 
720
716
    def test_diff_text(self):
721
717
        self.build_tree_contents([('old-tree/olddir/',),
722
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
718
                                  ('old-tree/olddir/oldfile', b'old\n')])
723
719
        self.old_tree.add('olddir')
724
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
720
        self.old_tree.add('olddir/oldfile', b'file-id')
725
721
        self.build_tree_contents([('new-tree/newdir/',),
726
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
722
                                  ('new-tree/newdir/newfile', b'new\n')])
727
723
        self.new_tree.add('newdir')
728
 
        self.new_tree.add('newdir/newfile', 'file-id')
 
724
        self.new_tree.add('newdir/newfile', b'file-id')
729
725
        differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
730
 
        differ.diff_text('olddir/oldfile', None, 'old label',
731
 
                         'new label', 'file-id', None)
 
726
        differ.diff_text('olddir/oldfile', None, 'old label', 'new label')
732
727
        self.assertEqual(
733
 
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
 
728
            b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
734
729
            differ.to_file.getvalue())
735
730
        differ.to_file.seek(0)
736
731
        differ.diff_text(None, 'newdir/newfile',
737
 
                         'old label', 'new label', None, 'file-id')
 
732
                         'old label', 'new label')
738
733
        self.assertEqual(
739
 
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
 
734
            b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
740
735
            differ.to_file.getvalue())
741
736
        differ.to_file.seek(0)
742
737
        differ.diff_text('olddir/oldfile', 'newdir/newfile',
743
 
                         'old label', 'new label', 'file-id', 'file-id')
 
738
                         'old label', 'new label')
744
739
        self.assertEqual(
745
 
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
 
740
            b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
746
741
            differ.to_file.getvalue())
747
742
 
748
743
    def test_diff_deletion(self):
749
 
        self.build_tree_contents([('old-tree/file', 'contents'),
750
 
                                  ('new-tree/file', 'contents')])
751
 
        self.old_tree.add('file', 'file-id')
752
 
        self.new_tree.add('file', 'file-id')
 
744
        self.build_tree_contents([('old-tree/file', b'contents'),
 
745
                                  ('new-tree/file', b'contents')])
 
746
        self.old_tree.add('file', b'file-id')
 
747
        self.new_tree.add('file', b'file-id')
753
748
        os.unlink('new-tree/file')
754
749
        self.differ.show_diff(None)
755
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
 
750
        self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
756
751
 
757
752
    def test_diff_creation(self):
758
 
        self.build_tree_contents([('old-tree/file', 'contents'),
759
 
                                  ('new-tree/file', 'contents')])
760
 
        self.old_tree.add('file', 'file-id')
761
 
        self.new_tree.add('file', 'file-id')
 
753
        self.build_tree_contents([('old-tree/file', b'contents'),
 
754
                                  ('new-tree/file', b'contents')])
 
755
        self.old_tree.add('file', b'file-id')
 
756
        self.new_tree.add('file', b'file-id')
762
757
        os.unlink('old-tree/file')
763
758
        self.differ.show_diff(None)
764
 
        self.assertContainsRe(self.differ.to_file.getvalue(), r'\+contents')
 
759
        self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
765
760
 
766
761
    def test_diff_symlink(self):
767
762
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
768
763
        differ.diff_symlink('old target', None)
769
 
        self.assertEqual("=== target was 'old target'\n",
 
764
        self.assertEqual(b"=== target was 'old target'\n",
770
765
                         differ.to_file.getvalue())
771
766
 
772
767
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
773
768
        differ.diff_symlink(None, 'new target')
774
 
        self.assertEqual("=== target is 'new target'\n",
 
769
        self.assertEqual(b"=== target is 'new target'\n",
775
770
                         differ.to_file.getvalue())
776
771
 
777
772
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
778
773
        differ.diff_symlink('old target', 'new target')
779
 
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
 
774
        self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
780
775
                         differ.to_file.getvalue())
781
776
 
782
777
    def test_diff(self):
783
778
        self.build_tree_contents([('old-tree/olddir/',),
784
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
779
                                  ('old-tree/olddir/oldfile', b'old\n')])
785
780
        self.old_tree.add('olddir')
786
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
781
        self.old_tree.add('olddir/oldfile', b'file-id')
787
782
        self.build_tree_contents([('new-tree/newdir/',),
788
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
783
                                  ('new-tree/newdir/newfile', b'new\n')])
789
784
        self.new_tree.add('newdir')
790
 
        self.new_tree.add('newdir/newfile', 'file-id')
791
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
785
        self.new_tree.add('newdir/newfile', b'file-id')
 
786
        self.differ.diff('olddir/oldfile', 'newdir/newfile')
792
787
        self.assertContainsRe(
793
788
            self.differ.to_file.getvalue(),
794
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
795
 
            r' \@\@\n-old\n\+new\n\n')
 
789
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
790
            br' \@\@\n-old\n\+new\n\n')
796
791
 
797
792
    def test_diff_kind_change(self):
798
793
        self.requireFeature(features.SymlinkFeature)
799
794
        self.build_tree_contents([('old-tree/olddir/',),
800
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
795
                                  ('old-tree/olddir/oldfile', b'old\n')])
801
796
        self.old_tree.add('olddir')
802
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
797
        self.old_tree.add('olddir/oldfile', b'file-id')
803
798
        self.build_tree(['new-tree/newdir/'])
804
799
        os.symlink('new', 'new-tree/newdir/newfile')
805
800
        self.new_tree.add('newdir')
806
 
        self.new_tree.add('newdir/newfile', 'file-id')
807
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
801
        self.new_tree.add('newdir/newfile', b'file-id')
 
802
        self.differ.diff('olddir/oldfile', 'newdir/newfile')
808
803
        self.assertContainsRe(
809
804
            self.differ.to_file.getvalue(),
810
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
811
 
            r' \@\@\n-old\n\n')
 
805
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
 
806
            br' \@\@\n-old\n\n')
812
807
        self.assertContainsRe(self.differ.to_file.getvalue(),
813
 
                              "=== target is u'new'\n")
 
808
                              b"=== target is 'new'\n")
814
809
 
815
810
    def test_diff_directory(self):
816
811
        self.build_tree(['new-tree/new-dir/'])
817
 
        self.new_tree.add('new-dir', 'new-dir-id')
818
 
        self.differ.diff('new-dir-id', None, 'new-dir')
819
 
        self.assertEqual(self.differ.to_file.getvalue(), '')
 
812
        self.new_tree.add('new-dir', b'new-dir-id')
 
813
        self.differ.diff(None, 'new-dir')
 
814
        self.assertEqual(self.differ.to_file.getvalue(), b'')
820
815
 
821
816
    def create_old_new(self):
822
817
        self.build_tree_contents([('old-tree/olddir/',),
823
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
818
                                  ('old-tree/olddir/oldfile', b'old\n')])
824
819
        self.old_tree.add('olddir')
825
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
820
        self.old_tree.add('olddir/oldfile', b'file-id')
826
821
        self.build_tree_contents([('new-tree/newdir/',),
827
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
822
                                  ('new-tree/newdir/newfile', b'new\n')])
828
823
        self.new_tree.add('newdir')
829
 
        self.new_tree.add('newdir/newfile', 'file-id')
 
824
        self.new_tree.add('newdir/newfile', b'file-id')
830
825
 
831
826
    def test_register_diff(self):
832
827
        self.create_old_new()
833
828
        old_diff_factories = diff.DiffTree.diff_factories
834
 
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
829
        diff.DiffTree.diff_factories = old_diff_factories[:]
835
830
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
836
831
        try:
837
832
            differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
838
833
        finally:
839
834
            diff.DiffTree.diff_factories = old_diff_factories
840
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
835
        differ.diff('olddir/oldfile', 'newdir/newfile')
841
836
        self.assertNotContainsRe(
842
837
            differ.to_file.getvalue(),
843
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
844
 
            r' \@\@\n-old\n\+new\n\n')
 
838
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
839
            br' \@\@\n-old\n\+new\n\n')
845
840
        self.assertContainsRe(differ.to_file.getvalue(),
846
 
                              'was: old\nis: new\n')
 
841
                              b'was: old\nis: new\n')
847
842
 
848
843
    def test_extra_factories(self):
849
844
        self.create_old_new()
850
845
        differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
851
846
                               extra_factories=[DiffWasIs.from_diff_tree])
852
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
847
        differ.diff('olddir/oldfile', 'newdir/newfile')
853
848
        self.assertNotContainsRe(
854
849
            differ.to_file.getvalue(),
855
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
856
 
            r' \@\@\n-old\n\+new\n\n')
 
850
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
851
            br' \@\@\n-old\n\+new\n\n')
857
852
        self.assertContainsRe(differ.to_file.getvalue(),
858
 
                              'was: old\nis: new\n')
 
853
                              b'was: old\nis: new\n')
859
854
 
860
855
    def test_alphabetical_order(self):
861
856
        self.build_tree(['new-tree/a-file'])
864
859
        self.old_tree.add('b-file')
865
860
        self.differ.show_diff(None)
866
861
        self.assertContainsRe(self.differ.to_file.getvalue(),
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)
 
862
                              b'.*a-file(.|\n)*b-file')
1397
863
 
1398
864
 
1399
865
class TestDiffFromTool(tests.TestCaseWithTransport):
1400
866
 
1401
867
    def test_from_string(self):
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)
 
868
        diff_obj = diff.DiffFromTool.from_string(
 
869
            ['diff', '{old_path}', '{new_path}'],
 
870
            None, None, None)
 
871
        self.addCleanup(diff_obj.finish)
 
872
        self.assertEqual(['diff', '{old_path}', '{new_path}'],
 
873
                         diff_obj.command_template)
 
874
 
 
875
    def test_from_string_no_paths(self):
 
876
        diff_obj = diff.DiffFromTool.from_string(
 
877
            ['diff', "-u5"], None, None, None)
 
878
        self.addCleanup(diff_obj.finish)
 
879
        self.assertEqual(['diff', '-u5'],
 
880
                         diff_obj.command_template)
 
881
        self.assertEqual(['diff', '-u5', 'old-path', 'new-path'],
 
882
                         diff_obj._get_command('old-path', 'new-path'))
1406
883
 
1407
884
    def test_from_string_u5(self):
1408
 
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
1409
 
                                                 None, None, None)
 
885
        diff_obj = diff.DiffFromTool.from_string(
 
886
            ['diff', "-u 5", '{old_path}', '{new_path}'], None, None, None)
1410
887
        self.addCleanup(diff_obj.finish)
1411
 
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
 
888
        self.assertEqual(['diff', '-u 5', '{old_path}', '{new_path}'],
1412
889
                         diff_obj.command_template)
1413
890
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1414
891
                         diff_obj._get_command('old-path', 'new-path'))
1415
892
 
1416
893
    def test_from_string_path_with_backslashes(self):
1417
894
        self.requireFeature(features.backslashdir_feature)
1418
 
        tool = 'C:\\Tools\\Diff.exe'
 
895
        tool = ['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}']
1419
896
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1420
897
        self.addCleanup(diff_obj.finish)
1421
 
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
898
        self.assertEqual(['C:\\Tools\\Diff.exe', '{old_path}', '{new_path}'],
1422
899
                         diff_obj.command_template)
1423
900
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
1424
901
                         diff_obj._get_command('old-path', 'new-path'))
1425
902
 
1426
903
    def test_execute(self):
1427
904
        output = BytesIO()
1428
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1429
 
                                      'print "@old_path @new_path"'],
 
905
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
 
906
                                      'print("{old_path} {new_path}")'],
1430
907
                                     None, None, output)
1431
908
        self.addCleanup(diff_obj.finish)
1432
909
        diff_obj._execute('old', 'new')
1433
 
        self.assertEqual(output.getvalue().rstrip(), 'old new')
 
910
        self.assertEqual(output.getvalue().rstrip(), b'old new')
1434
911
 
1435
912
    def test_execute_missing(self):
1436
913
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1445
922
        self.requireFeature(features.AttribFeature)
1446
923
        output = BytesIO()
1447
924
        tree = self.make_branch_and_tree('tree')
1448
 
        self.build_tree_contents([('tree/file', 'content')])
1449
 
        tree.add('file', 'file-id')
 
925
        self.build_tree_contents([('tree/file', b'content')])
 
926
        tree.add('file', b'file-id')
1450
927
        tree.commit('old tree')
1451
928
        tree.lock_read()
1452
929
        self.addCleanup(tree.unlock)
1453
930
        basis_tree = tree.basis_tree()
1454
931
        basis_tree.lock_read()
1455
932
        self.addCleanup(basis_tree.unlock)
1456
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1457
 
                                      'print "@old_path @new_path"'],
 
933
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
 
934
                                      'print "{old_path} {new_path}"'],
1458
935
                                     basis_tree, tree, output)
1459
 
        diff_obj._prepare_files('file', 'file', file_id='file-id')
 
936
        diff_obj._prepare_files('file', 'file', file_id=b'file-id')
1460
937
        # The old content should be readonly
1461
938
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1462
939
                                    r'R.*old\\file$')
1474
951
    def test_prepare_files(self):
1475
952
        output = BytesIO()
1476
953
        tree = self.make_branch_and_tree('tree')
1477
 
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1478
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1479
 
        tree.add('oldname', 'file-id')
1480
 
        tree.add('oldname2', 'file2-id')
 
954
        self.build_tree_contents([('tree/oldname', b'oldcontent')])
 
955
        self.build_tree_contents([('tree/oldname2', b'oldcontent2')])
 
956
        tree.add('oldname', b'file-id')
 
957
        tree.add('oldname2', b'file2-id')
1481
958
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1482
959
        tree.commit('old tree', timestamp=315532800)
1483
960
        tree.rename_one('oldname', 'newname')
1484
961
        tree.rename_one('oldname2', 'newname2')
1485
 
        self.build_tree_contents([('tree/newname', 'newcontent')])
1486
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
 
962
        self.build_tree_contents([('tree/newname', b'newcontent')])
 
963
        self.build_tree_contents([('tree/newname2', b'newcontent2')])
1487
964
        old_tree = tree.basis_tree()
1488
965
        old_tree.lock_read()
1489
966
        self.addCleanup(old_tree.unlock)
1490
967
        tree.lock_read()
1491
968
        self.addCleanup(tree.unlock)
1492
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1493
 
                                      'print "@old_path @new_path"'],
 
969
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
 
970
                                      'print "{old_path} {new_path}"'],
1494
971
                                     old_tree, tree, output)
1495
972
        self.addCleanup(diff_obj.finish)
1496
973
        self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
1497
974
        old_path, new_path = diff_obj._prepare_files(
1498
 
                'oldname', 'newname', file_id='file-id')
 
975
            'oldname', 'newname')
1499
976
        self.assertContainsRe(old_path, 'old/oldname$')
1500
977
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1501
978
        self.assertContainsRe(new_path, 'tree/newname$')
1502
 
        self.assertFileEqual('oldcontent', old_path)
1503
 
        self.assertFileEqual('newcontent', new_path)
 
979
        self.assertFileEqual(b'oldcontent', old_path)
 
980
        self.assertFileEqual(b'newcontent', new_path)
1504
981
        if osutils.host_os_dereferences_symlinks():
1505
982
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1506
983
        # make sure we can create files with the same parent directories
1507
 
        diff_obj._prepare_files('oldname2', 'newname2', file_id='file2-id')
 
984
        diff_obj._prepare_files('oldname2', 'newname2')
1508
985
 
1509
986
 
1510
987
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1512
989
    def test_encodable_filename(self):
1513
990
        # Just checks file path for external diff tool.
1514
991
        # We cannot change CPython's internal encoding used by os.exec*.
1515
 
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
992
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
1516
993
                                    None, None, None)
1517
994
        for _, scenario in EncodingAdapter.encoding_scenarios:
1518
995
            encoding = scenario['encoding']
1527
1004
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1528
1005
 
1529
1006
    def test_unencodable_filename(self):
1530
 
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1007
        diffobj = diff.DiffFromTool(['dummy', '{old_path}', '{new_path}'],
1531
1008
                                    None, None, None)
1532
1009
        for _, scenario in EncodingAdapter.encoding_scenarios:
1533
1010
            encoding = scenario['encoding']
1551
1028
 
1552
1029
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1553
1030
        """Call get_trees_and_branches_to_diff_locked."""
 
1031
        exit_stack = contextlib.ExitStack()
 
1032
        self.addCleanup(exit_stack.close)
1554
1033
        return diff.get_trees_and_branches_to_diff_locked(
1555
 
            path_list, revision_specs, old_url, new_url, self.addCleanup)
 
1034
            path_list, revision_specs, old_url, new_url, exit_stack)
1556
1035
 
1557
1036
    def test_basic(self):
1558
1037
        tree = self.make_branch_and_tree('tree')
1572
1051
 
1573
1052
    def test_with_rev_specs(self):
1574
1053
        tree = self.make_branch_and_tree('tree')
1575
 
        self.build_tree_contents([('tree/file', 'oldcontent')])
1576
 
        tree.add('file', 'file-id')
1577
 
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1578
 
        self.build_tree_contents([('tree/file', 'newcontent')])
1579
 
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1054
        self.build_tree_contents([('tree/file', b'oldcontent')])
 
1055
        tree.add('file', b'file-id')
 
1056
        tree.commit('old tree', timestamp=0, rev_id=b"old-id")
 
1057
        self.build_tree_contents([('tree/file', b'newcontent')])
 
1058
        tree.commit('new tree', timestamp=0, rev_id=b"new-id")
1580
1059
 
1581
1060
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1582
1061
                     revisionspec.RevisionSpec.from_string('2')]
1586
1065
            ['tree'], revisions, None, None)
1587
1066
 
1588
1067
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1589
 
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1068
        self.assertEqual(b"old-id", old_tree.get_revision_id())
1590
1069
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1591
 
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1070
        self.assertEqual(b"new-id", new_tree.get_revision_id())
1592
1071
        self.assertEqual(tree.branch.base, old_branch.base)
1593
1072
        self.assertEqual(tree.branch.base, new_branch.base)
1594
1073
        self.assertIs(None, specific_files)