/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: Martin
  • Date: 2018-11-16 16:38:22 UTC
  • mto: This revision was merged to the branch mainline in revision 7172.
  • Revision ID: gzlist@googlemail.com-20181116163822-yg1h1cdng6w7w9kn
Make --profile-imports work on Python 3

Also tweak heading to line up correctly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2014, 2016, 2017 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
from cStringIO import StringIO
 
18
import re
19
19
import subprocess
20
20
import sys
21
21
import tempfile
22
22
 
23
 
from bzrlib import (
 
23
from .. import (
24
24
    diff,
25
25
    errors,
26
26
    osutils,
32
32
    tests,
33
33
    transform,
34
34
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import test_win32utils
37
 
 
38
 
 
39
 
class _AttribFeature(tests.Feature):
40
 
 
41
 
    def _probe(self):
42
 
        if (sys.platform not in ('cygwin', 'win32')):
43
 
            return False
44
 
        try:
45
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
46
 
        except OSError, e:
47
 
            return False
48
 
        return (0 == proc.wait())
49
 
 
50
 
    def feature_name(self):
51
 
        return 'attrib Windows command-line tool'
52
 
 
53
 
AttribFeature = _AttribFeature()
54
 
 
55
 
 
56
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
57
 
                                    'bzrlib._patiencediff_c')
 
35
from ..sixish import (
 
36
    BytesIO,
 
37
    unichr,
 
38
    )
 
39
from ..tests import (
 
40
    features,
 
41
    EncodingAdapter,
 
42
    )
 
43
from ..tests.scenarios import load_tests_apply_scenarios
 
44
 
 
45
 
 
46
load_tests = load_tests_apply_scenarios
 
47
 
 
48
 
 
49
def subst_dates(string):
 
50
    """Replace date strings with constant values."""
 
51
    return re.sub(br'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
 
52
                  b'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
58
53
 
59
54
 
60
55
def udiff_lines(old, new, allow_binary=False):
61
 
    output = StringIO()
 
56
    output = BytesIO()
62
57
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
63
58
    output.seek(0, 0)
64
59
    return output.readlines()
66
61
 
67
62
def external_udiff_lines(old, new, use_stringio=False):
68
63
    if use_stringio:
69
 
        # StringIO has no fileno, so it tests a different codepath
70
 
        output = StringIO()
 
64
        # BytesIO has no fileno, so it tests a different codepath
 
65
        output = BytesIO()
71
66
    else:
72
67
        output = tempfile.TemporaryFile()
73
68
    try:
80
75
    return lines
81
76
 
82
77
 
 
78
class StubO(object):
 
79
    """Simple file-like object that allows writes with any type and records."""
 
80
 
 
81
    def __init__(self):
 
82
        self.write_record = []
 
83
 
 
84
    def write(self, data):
 
85
        self.write_record.append(data)
 
86
 
 
87
    def check_types(self, testcase, expected_type):
 
88
        testcase.assertFalse(
 
89
            any(not isinstance(o, expected_type) for o in self.write_record),
 
90
            "Not all writes of type %s: %r" % (
 
91
                expected_type.__name__, self.write_record))
 
92
 
 
93
 
 
94
class TestDiffOptions(tests.TestCase):
 
95
 
 
96
    def test_unified_added(self):
 
97
        """Check for default style '-u' only if no other style specified
 
98
        in 'diff-options'.
 
99
        """
 
100
        # Verify that style defaults to unified, id est '-u' appended
 
101
        # to option list, in the absence of an alternative style.
 
102
        self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
 
103
 
 
104
 
 
105
class TestDiffOptionsScenarios(tests.TestCase):
 
106
 
 
107
    scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
 
108
    style = None # Set by load_tests_apply_scenarios from scenarios
 
109
 
 
110
    def test_unified_not_added(self):
 
111
        # Verify that for all valid style options, '-u' is not
 
112
        # appended to option list.
 
113
        ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
 
114
        self.assertEqual(["%s" % (self.style,)], ret_opts)
 
115
 
 
116
 
83
117
class TestDiff(tests.TestCase):
84
118
 
85
119
    def test_add_nl(self):
86
120
        """diff generates a valid diff for patches that add a newline"""
87
 
        lines = udiff_lines(['boo'], ['boo\n'])
 
121
        lines = udiff_lines([b'boo'], [b'boo\n'])
88
122
        self.check_patch(lines)
89
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
123
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
90
124
            ## "expected no-nl, got %r" % lines[4]
91
125
 
92
126
    def test_add_nl_2(self):
93
127
        """diff generates a valid diff for patches that change last line and
94
128
        add a newline.
95
129
        """
96
 
        lines = udiff_lines(['boo'], ['goo\n'])
 
130
        lines = udiff_lines([b'boo'], [b'goo\n'])
97
131
        self.check_patch(lines)
98
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
132
        self.assertEqual(lines[4], b'\\ No newline at end of file\n')
99
133
            ## "expected no-nl, got %r" % lines[4]
100
134
 
101
135
    def test_remove_nl(self):
102
136
        """diff generates a valid diff for patches that change last line and
103
137
        add a newline.
104
138
        """
105
 
        lines = udiff_lines(['boo\n'], ['boo'])
 
139
        lines = udiff_lines([b'boo\n'], [b'boo'])
106
140
        self.check_patch(lines)
107
 
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
 
141
        self.assertEqual(lines[5], b'\\ No newline at end of file\n')
108
142
            ## "expected no-nl, got %r" % lines[5]
109
143
 
110
144
    def check_patch(self, lines):
111
 
        self.assert_(len(lines) > 1)
 
145
        self.assertTrue(len(lines) > 1)
112
146
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
113
 
        self.assert_(lines[0].startswith ('---'))
 
147
        self.assertTrue(lines[0].startswith (b'---'))
114
148
            ## 'No orig line for patch:\n%s' % "".join(lines)
115
 
        self.assert_(lines[1].startswith ('+++'))
 
149
        self.assertTrue(lines[1].startswith (b'+++'))
116
150
            ## 'No mod line for patch:\n%s' % "".join(lines)
117
 
        self.assert_(len(lines) > 2)
 
151
        self.assertTrue(len(lines) > 2)
118
152
            ## "No hunks for patch:\n%s" % "".join(lines)
119
 
        self.assert_(lines[2].startswith('@@'))
 
153
        self.assertTrue(lines[2].startswith(b'@@'))
120
154
            ## "No hunk header for patch:\n%s" % "".join(lines)
121
 
        self.assert_('@@' in lines[2][2:])
 
155
        self.assertTrue(b'@@' in lines[2][2:])
122
156
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
123
157
 
124
158
    def test_binary_lines(self):
125
159
        empty = []
126
 
        uni_lines = [1023 * 'a' + '\x00']
127
 
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
 
160
        uni_lines = [1023 * b'a' + b'\x00']
 
161
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
162
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
 
        udiff_lines(uni_lines , empty, allow_binary=True)
 
163
        udiff_lines(uni_lines, empty, allow_binary=True)
130
164
        udiff_lines(empty, uni_lines, allow_binary=True)
131
165
 
132
166
    def test_external_diff(self):
133
 
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
 
167
        lines = external_udiff_lines([b'boo\n'], [b'goo\n'])
134
168
        self.check_patch(lines)
135
 
        self.assertEqual('\n', lines[-1])
 
169
        self.assertEqual(b'\n', lines[-1])
136
170
 
137
171
    def test_external_diff_no_fileno(self):
138
172
        # Make sure that we can handle not having a fileno, even
139
173
        # if the diff is large
140
 
        lines = external_udiff_lines(['boo\n']*10000,
141
 
                                     ['goo\n']*10000,
 
174
        lines = external_udiff_lines([b'boo\n']*10000,
 
175
                                     [b'goo\n']*10000,
142
176
                                     use_stringio=True)
143
177
        self.check_patch(lines)
144
178
 
145
179
    def test_external_diff_binary_lang_c(self):
146
 
        old_env = {}
147
180
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
149
 
        try:
150
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
151
 
            # Older versions of diffutils say "Binary files", newer
152
 
            # versions just say "Files".
153
 
            self.assertContainsRe(lines[0],
154
 
                                  '(Binary f|F)iles old and new differ\n')
155
 
            self.assertEquals(lines[1:], ['\n'])
156
 
        finally:
157
 
            for lang, old_val in old_env.iteritems():
158
 
                osutils.set_or_unset_env(lang, old_val)
 
181
            self.overrideEnv(lang, 'C')
 
182
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
 
183
        # Older versions of diffutils say "Binary files", newer
 
184
        # versions just say "Files".
 
185
        self.assertContainsRe(lines[0], b'(Binary f|F)iles old and new differ\n')
 
186
        self.assertEqual(lines[1:], [b'\n'])
159
187
 
160
188
    def test_no_external_diff(self):
161
189
        """Check that NoDiff is raised when diff is not available"""
162
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
163
 
        orig_path = os.environ['PATH']
164
 
        try:
165
 
            os.environ['PATH'] = ''
166
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
167
 
                              'old', ['boo\n'], 'new', ['goo\n'],
168
 
                              StringIO(), diff_opts=['-u'])
169
 
        finally:
170
 
            os.environ['PATH'] = orig_path
 
190
        # Make sure no 'diff' command is available
 
191
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
 
192
        self.overrideEnv('PATH', '')
 
193
        self.assertRaises(errors.NoDiff, diff.external_diff,
 
194
                          b'old', [b'boo\n'], b'new', [b'goo\n'],
 
195
                          BytesIO(), diff_opts=['-u'])
171
196
 
172
197
    def test_internal_diff_default(self):
173
198
        # Default internal diff encoding is utf8
174
 
        output = StringIO()
175
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
 
                           u'new_\xe5', ['new_text\n'], output)
 
199
        output = BytesIO()
 
200
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
201
                           u'new_\xe5', [b'new_text\n'], output)
177
202
        lines = output.getvalue().splitlines(True)
178
203
        self.check_patch(lines)
179
 
        self.assertEquals(['--- old_\xc2\xb5\n',
180
 
                           '+++ new_\xc3\xa5\n',
181
 
                           '@@ -1,1 +1,1 @@\n',
182
 
                           '-old_text\n',
183
 
                           '+new_text\n',
184
 
                           '\n',
 
204
        self.assertEqual([b'--- old_\xc2\xb5\n',
 
205
                           b'+++ new_\xc3\xa5\n',
 
206
                           b'@@ -1,1 +1,1 @@\n',
 
207
                           b'-old_text\n',
 
208
                           b'+new_text\n',
 
209
                           b'\n',
185
210
                          ]
186
211
                          , lines)
187
212
 
188
213
    def test_internal_diff_utf8(self):
189
 
        output = StringIO()
190
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
191
 
                           u'new_\xe5', ['new_text\n'], output,
 
214
        output = BytesIO()
 
215
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
216
                           u'new_\xe5', [b'new_text\n'], output,
192
217
                           path_encoding='utf8')
193
218
        lines = output.getvalue().splitlines(True)
194
219
        self.check_patch(lines)
195
 
        self.assertEquals(['--- old_\xc2\xb5\n',
196
 
                           '+++ new_\xc3\xa5\n',
197
 
                           '@@ -1,1 +1,1 @@\n',
198
 
                           '-old_text\n',
199
 
                           '+new_text\n',
200
 
                           '\n',
 
220
        self.assertEqual([b'--- old_\xc2\xb5\n',
 
221
                           b'+++ new_\xc3\xa5\n',
 
222
                           b'@@ -1,1 +1,1 @@\n',
 
223
                           b'-old_text\n',
 
224
                           b'+new_text\n',
 
225
                           b'\n',
201
226
                          ]
202
227
                          , lines)
203
228
 
204
229
    def test_internal_diff_iso_8859_1(self):
205
 
        output = StringIO()
206
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
207
 
                           u'new_\xe5', ['new_text\n'], output,
 
230
        output = BytesIO()
 
231
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
232
                           u'new_\xe5', [b'new_text\n'], output,
208
233
                           path_encoding='iso-8859-1')
209
234
        lines = output.getvalue().splitlines(True)
210
235
        self.check_patch(lines)
211
 
        self.assertEquals(['--- old_\xb5\n',
212
 
                           '+++ new_\xe5\n',
213
 
                           '@@ -1,1 +1,1 @@\n',
214
 
                           '-old_text\n',
215
 
                           '+new_text\n',
216
 
                           '\n',
 
236
        self.assertEqual([b'--- old_\xb5\n',
 
237
                          b'+++ new_\xe5\n',
 
238
                          b'@@ -1,1 +1,1 @@\n',
 
239
                          b'-old_text\n',
 
240
                          b'+new_text\n',
 
241
                          b'\n',
217
242
                          ]
218
243
                          , lines)
219
244
 
220
245
    def test_internal_diff_no_content(self):
221
 
        output = StringIO()
 
246
        output = BytesIO()
222
247
        diff.internal_diff(u'old', [], u'new', [], output)
223
 
        self.assertEqual('', output.getvalue())
 
248
        self.assertEqual(b'', output.getvalue())
224
249
 
225
250
    def test_internal_diff_no_changes(self):
226
 
        output = StringIO()
227
 
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
 
                           u'new', ['text\n', 'contents\n'],
 
251
        output = BytesIO()
 
252
        diff.internal_diff(u'old', [b'text\n', b'contents\n'],
 
253
                           u'new', [b'text\n', b'contents\n'],
229
254
                           output)
230
 
        self.assertEqual('', output.getvalue())
 
255
        self.assertEqual(b'', output.getvalue())
231
256
 
232
257
    def test_internal_diff_returns_bytes(self):
233
 
        import StringIO
234
 
        output = StringIO.StringIO()
235
 
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
 
                            u'new_\xe5', ['new_text\n'], output)
237
 
        self.failUnless(isinstance(output.getvalue(), str),
238
 
            'internal_diff should return bytestrings')
 
258
        output = StubO()
 
259
        diff.internal_diff(u'old_\xb5', [b'old_text\n'],
 
260
                           u'new_\xe5', [b'new_text\n'], output)
 
261
        output.check_types(self, bytes)
 
262
 
 
263
    def test_internal_diff_default_context(self):
 
264
        output = BytesIO()
 
265
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
266
                           b'same_text\n', b'same_text\n', b'old_text\n'],
 
267
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
268
                           b'same_text\n', b'same_text\n', b'new_text\n'], output)
 
269
        lines = output.getvalue().splitlines(True)
 
270
        self.check_patch(lines)
 
271
        self.assertEqual([b'--- old\n',
 
272
                           b'+++ new\n',
 
273
                           b'@@ -3,4 +3,4 @@\n',
 
274
                           b' same_text\n',
 
275
                           b' same_text\n',
 
276
                           b' same_text\n',
 
277
                           b'-old_text\n',
 
278
                           b'+new_text\n',
 
279
                           b'\n',
 
280
                          ]
 
281
                          , lines)
 
282
 
 
283
    def test_internal_diff_no_context(self):
 
284
        output = BytesIO()
 
285
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
286
                           b'same_text\n', b'same_text\n', b'old_text\n'],
 
287
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
288
                           b'same_text\n', b'same_text\n', b'new_text\n'], output,
 
289
                           context_lines=0)
 
290
        lines = output.getvalue().splitlines(True)
 
291
        self.check_patch(lines)
 
292
        self.assertEqual([b'--- old\n',
 
293
                           b'+++ new\n',
 
294
                           b'@@ -6,1 +6,1 @@\n',
 
295
                           b'-old_text\n',
 
296
                           b'+new_text\n',
 
297
                           b'\n',
 
298
                          ]
 
299
                          , lines)
 
300
 
 
301
    def test_internal_diff_more_context(self):
 
302
        output = BytesIO()
 
303
        diff.internal_diff('old', [b'same_text\n', b'same_text\n', b'same_text\n',
 
304
                           b'same_text\n', b'same_text\n', b'old_text\n'],
 
305
                           'new', [b'same_text\n', b'same_text\n', b'same_text\n',
 
306
                           b'same_text\n', b'same_text\n', b'new_text\n'], output,
 
307
                           context_lines=4)
 
308
        lines = output.getvalue().splitlines(True)
 
309
        self.check_patch(lines)
 
310
        self.assertEqual([b'--- old\n',
 
311
                          b'+++ new\n',
 
312
                          b'@@ -2,5 +2,5 @@\n',
 
313
                          b' same_text\n',
 
314
                          b' same_text\n',
 
315
                          b' same_text\n',
 
316
                          b' same_text\n',
 
317
                          b'-old_text\n',
 
318
                          b'+new_text\n',
 
319
                          b'\n',
 
320
                          ]
 
321
                          , lines)
 
322
 
239
323
 
240
324
 
241
325
class TestDiffFiles(tests.TestCaseInTempDir):
242
326
 
243
327
    def test_external_diff_binary(self):
244
328
        """The output when using external diff should use diff's i18n error"""
 
329
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
 
330
            self.overrideEnv(lang, 'C')
245
331
        # Make sure external_diff doesn't fail in the current LANG
246
 
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
332
        lines = external_udiff_lines([b'\x00foobar\n'], [b'foo\x00bar\n'])
247
333
 
248
334
        cmd = ['diff', '-u', '--binary', 'old', 'new']
249
 
        open('old', 'wb').write('\x00foobar\n')
250
 
        open('new', 'wb').write('foo\x00bar\n')
 
335
        with open('old', 'wb') as f: f.write(b'\x00foobar\n')
 
336
        with open('new', 'wb') as f: f.write(b'foo\x00bar\n')
251
337
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
338
                                     stdin=subprocess.PIPE)
253
339
        out, err = pipe.communicate()
254
 
        # Diff returns '2' on Binary files.
255
 
        self.assertEqual(2, pipe.returncode)
256
340
        # We should output whatever diff tells us, plus a trailing newline
257
 
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
258
 
 
259
 
 
260
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
261
 
    """Has a helper for running show_diff_trees"""
262
 
 
263
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
264
 
        output = StringIO()
265
 
        if working_tree is not None:
266
 
            extra_trees = (working_tree,)
267
 
        else:
268
 
            extra_trees = ()
269
 
        diff.show_diff_trees(tree1, tree2, output,
270
 
                             specific_files=specific_files,
271
 
                             extra_trees=extra_trees, old_label='old/',
272
 
                             new_label='new/')
273
 
        return output.getvalue()
274
 
 
275
 
 
276
 
class TestDiffDates(TestShowDiffTreesHelper):
 
341
        self.assertEqual(out.splitlines(True) + [b'\n'], lines)
 
342
 
 
343
 
 
344
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
345
    output = BytesIO()
 
346
    if working_tree is not None:
 
347
        extra_trees = (working_tree,)
 
348
    else:
 
349
        extra_trees = ()
 
350
    diff.show_diff_trees(tree1, tree2, output,
 
351
        specific_files=specific_files,
 
352
        extra_trees=extra_trees, old_label='old/',
 
353
        new_label='new/')
 
354
    return output.getvalue()
 
355
 
 
356
 
 
357
class TestDiffDates(tests.TestCaseWithTransport):
277
358
 
278
359
    def setUp(self):
279
360
        super(TestDiffDates, self).setUp()
280
361
        self.wt = self.make_branch_and_tree('.')
281
362
        self.b = self.wt.branch
282
363
        self.build_tree_contents([
283
 
            ('file1', 'file1 contents at rev 1\n'),
284
 
            ('file2', 'file2 contents at rev 1\n')
 
364
            ('file1', b'file1 contents at rev 1\n'),
 
365
            ('file2', b'file2 contents at rev 1\n')
285
366
            ])
286
367
        self.wt.add(['file1', 'file2'])
287
368
        self.wt.commit(
288
369
            message='Revision 1',
289
370
            timestamp=1143849600, # 2006-04-01 00:00:00 UTC
290
371
            timezone=0,
291
 
            rev_id='rev-1')
292
 
        self.build_tree_contents([('file1', 'file1 contents at rev 2\n')])
 
372
            rev_id=b'rev-1')
 
373
        self.build_tree_contents([('file1', b'file1 contents at rev 2\n')])
293
374
        self.wt.commit(
294
375
            message='Revision 2',
295
376
            timestamp=1143936000, # 2006-04-02 00:00:00 UTC
296
377
            timezone=28800,
297
 
            rev_id='rev-2')
298
 
        self.build_tree_contents([('file2', 'file2 contents at rev 3\n')])
 
378
            rev_id=b'rev-2')
 
379
        self.build_tree_contents([('file2', b'file2 contents at rev 3\n')])
299
380
        self.wt.commit(
300
381
            message='Revision 3',
301
382
            timestamp=1144022400, # 2006-04-03 00:00:00 UTC
302
383
            timezone=-3600,
303
 
            rev_id='rev-3')
 
384
            rev_id=b'rev-3')
304
385
        self.wt.remove(['file2'])
305
386
        self.wt.commit(
306
387
            message='Revision 4',
307
388
            timestamp=1144108800, # 2006-04-04 00:00:00 UTC
308
389
            timezone=0,
309
 
            rev_id='rev-4')
 
390
            rev_id=b'rev-4')
310
391
        self.build_tree_contents([
311
 
            ('file1', 'file1 contents in working tree\n')
 
392
            ('file1', b'file1 contents in working tree\n')
312
393
            ])
313
394
        # set the date stamps for files in the working tree to known values
314
395
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
315
396
 
316
397
    def test_diff_rev_tree_working_tree(self):
317
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
398
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
399
        # note that the date for old/file1 is from rev 2 rather than from
319
400
        # the basis revision (rev 4)
320
 
        self.assertEqualDiff(output, '''\
 
401
        self.assertEqualDiff(output, b'''\
321
402
=== modified file 'file1'
322
403
--- old/file1\t2006-04-02 00:00:00 +0000
323
404
+++ new/file1\t2006-04-05 00:00:00 +0000
328
409
''')
329
410
 
330
411
    def test_diff_rev_tree_rev_tree(self):
331
 
        tree1 = self.b.repository.revision_tree('rev-2')
332
 
        tree2 = self.b.repository.revision_tree('rev-3')
333
 
        output = self.get_diff(tree1, tree2)
334
 
        self.assertEqualDiff(output, '''\
 
412
        tree1 = self.b.repository.revision_tree(b'rev-2')
 
413
        tree2 = self.b.repository.revision_tree(b'rev-3')
 
414
        output = get_diff_as_string(tree1, tree2)
 
415
        self.assertEqualDiff(output, b'''\
335
416
=== modified file 'file2'
336
417
--- old/file2\t2006-04-01 00:00:00 +0000
337
418
+++ new/file2\t2006-04-03 00:00:00 +0000
343
424
 
344
425
    def test_diff_add_files(self):
345
426
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
346
 
        tree2 = self.b.repository.revision_tree('rev-1')
347
 
        output = self.get_diff(tree1, tree2)
 
427
        tree2 = self.b.repository.revision_tree(b'rev-1')
 
428
        output = get_diff_as_string(tree1, tree2)
348
429
        # the files have the epoch time stamp for the tree in which
349
430
        # they don't exist.
350
 
        self.assertEqualDiff(output, '''\
 
431
        self.assertEqualDiff(output, b'''\
351
432
=== added file 'file1'
352
433
--- old/file1\t1970-01-01 00:00:00 +0000
353
434
+++ new/file1\t2006-04-01 00:00:00 +0000
363
444
''')
364
445
 
365
446
    def test_diff_remove_files(self):
366
 
        tree1 = self.b.repository.revision_tree('rev-3')
367
 
        tree2 = self.b.repository.revision_tree('rev-4')
368
 
        output = self.get_diff(tree1, tree2)
 
447
        tree1 = self.b.repository.revision_tree(b'rev-3')
 
448
        tree2 = self.b.repository.revision_tree(b'rev-4')
 
449
        output = get_diff_as_string(tree1, tree2)
369
450
        # the file has the epoch time stamp for the tree in which
370
451
        # it doesn't exist.
371
 
        self.assertEqualDiff(output, '''\
 
452
        self.assertEqualDiff(output, b'''\
372
453
=== removed file 'file2'
373
454
--- old/file2\t2006-04-03 00:00:00 +0000
374
455
+++ new/file2\t1970-01-01 00:00:00 +0000
380
461
    def test_show_diff_specified(self):
381
462
        """A working tree filename can be used to identify a file"""
382
463
        self.wt.rename_one('file1', 'file1b')
383
 
        old_tree = self.b.repository.revision_tree('rev-1')
384
 
        new_tree = self.b.repository.revision_tree('rev-4')
385
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
464
        old_tree = self.b.repository.revision_tree(b'rev-1')
 
465
        new_tree = self.b.repository.revision_tree(b'rev-4')
 
466
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
386
467
                            working_tree=self.wt)
387
 
        self.assertContainsRe(out, 'file1\t')
 
468
        self.assertContainsRe(out, b'file1\t')
388
469
 
389
470
    def test_recursive_diff(self):
390
471
        """Children of directories are matched"""
392
473
        os.mkdir('dir2')
393
474
        self.wt.add(['dir1', 'dir2'])
394
475
        self.wt.rename_one('file1', 'dir1/file1')
395
 
        old_tree = self.b.repository.revision_tree('rev-1')
396
 
        new_tree = self.b.repository.revision_tree('rev-4')
397
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
398
 
                            working_tree=self.wt)
399
 
        self.assertContainsRe(out, 'file1\t')
400
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
401
 
                            working_tree=self.wt)
402
 
        self.assertNotContainsRe(out, 'file1\t')
403
 
 
404
 
 
405
 
 
406
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
476
        old_tree = self.b.repository.revision_tree(b'rev-1')
 
477
        new_tree = self.b.repository.revision_tree(b'rev-4')
 
478
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
 
479
                            working_tree=self.wt)
 
480
        self.assertContainsRe(out, b'file1\t')
 
481
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
 
482
                            working_tree=self.wt)
 
483
        self.assertNotContainsRe(out, b'file1\t')
 
484
 
 
485
 
 
486
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
487
    """Direct tests for show_diff_trees"""
408
488
 
409
489
    def test_modified_file(self):
410
490
        """Test when a file is modified."""
411
491
        tree = self.make_branch_and_tree('tree')
412
 
        self.build_tree_contents([('tree/file', 'contents\n')])
413
 
        tree.add(['file'], ['file-id'])
414
 
        tree.commit('one', rev_id='rev-1')
 
492
        self.build_tree_contents([('tree/file', b'contents\n')])
 
493
        tree.add(['file'], [b'file-id'])
 
494
        tree.commit('one', rev_id=b'rev-1')
415
495
 
416
 
        self.build_tree_contents([('tree/file', 'new contents\n')])
417
 
        d = self.get_diff(tree.basis_tree(), tree)
418
 
        self.assertContainsRe(d, "=== modified file 'file'\n")
419
 
        self.assertContainsRe(d, '--- old/file\t')
420
 
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
421
 
        self.assertContainsRe(d, '-contents\n'
422
 
                                 '\\+new contents\n')
 
496
        self.build_tree_contents([('tree/file', b'new contents\n')])
 
497
        d = get_diff_as_string(tree.basis_tree(), tree)
 
498
        self.assertContainsRe(d, b"=== modified file 'file'\n")
 
499
        self.assertContainsRe(d, b'--- old/file\t')
 
500
        self.assertContainsRe(d, b'\\+\\+\\+ new/file\t')
 
501
        self.assertContainsRe(d, b'-contents\n'
 
502
                                 b'\\+new contents\n')
423
503
 
424
504
    def test_modified_file_in_renamed_dir(self):
425
505
        """Test when a file is modified in a renamed directory."""
426
506
        tree = self.make_branch_and_tree('tree')
427
507
        self.build_tree(['tree/dir/'])
428
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
429
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
430
 
        tree.commit('one', rev_id='rev-1')
 
508
        self.build_tree_contents([('tree/dir/file', b'contents\n')])
 
509
        tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
 
510
        tree.commit('one', rev_id=b'rev-1')
431
511
 
432
512
        tree.rename_one('dir', 'other')
433
 
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
434
 
        d = self.get_diff(tree.basis_tree(), tree)
435
 
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
436
 
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
 
513
        self.build_tree_contents([('tree/other/file', b'new contents\n')])
 
514
        d = get_diff_as_string(tree.basis_tree(), tree)
 
515
        self.assertContainsRe(d, b"=== renamed directory 'dir' => 'other'\n")
 
516
        self.assertContainsRe(d, b"=== modified file 'other/file'\n")
437
517
        # XXX: This is technically incorrect, because it used to be at another
438
518
        # location. What to do?
439
 
        self.assertContainsRe(d, '--- old/dir/file\t')
440
 
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
441
 
        self.assertContainsRe(d, '-contents\n'
442
 
                                 '\\+new contents\n')
 
519
        self.assertContainsRe(d, b'--- old/dir/file\t')
 
520
        self.assertContainsRe(d, b'\\+\\+\\+ new/other/file\t')
 
521
        self.assertContainsRe(d, b'-contents\n'
 
522
                                 b'\\+new contents\n')
443
523
 
444
524
    def test_renamed_directory(self):
445
525
        """Test when only a directory is only renamed."""
446
526
        tree = self.make_branch_and_tree('tree')
447
527
        self.build_tree(['tree/dir/'])
448
 
        self.build_tree_contents([('tree/dir/file', 'contents\n')])
449
 
        tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
450
 
        tree.commit('one', rev_id='rev-1')
 
528
        self.build_tree_contents([('tree/dir/file', b'contents\n')])
 
529
        tree.add(['dir', 'dir/file'], [b'dir-id', b'file-id'])
 
530
        tree.commit('one', rev_id=b'rev-1')
451
531
 
452
532
        tree.rename_one('dir', 'newdir')
453
 
        d = self.get_diff(tree.basis_tree(), tree)
 
533
        d = get_diff_as_string(tree.basis_tree(), tree)
454
534
        # Renaming a directory should be a single "you renamed this dir" even
455
535
        # when there are files inside.
456
 
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
 
536
        self.assertEqual(d, b"=== renamed directory 'dir' => 'newdir'\n")
457
537
 
458
538
    def test_renamed_file(self):
459
539
        """Test when a file is only renamed."""
460
540
        tree = self.make_branch_and_tree('tree')
461
 
        self.build_tree_contents([('tree/file', 'contents\n')])
462
 
        tree.add(['file'], ['file-id'])
463
 
        tree.commit('one', rev_id='rev-1')
 
541
        self.build_tree_contents([('tree/file', b'contents\n')])
 
542
        tree.add(['file'], [b'file-id'])
 
543
        tree.commit('one', rev_id=b'rev-1')
464
544
 
465
545
        tree.rename_one('file', 'newname')
466
 
        d = self.get_diff(tree.basis_tree(), tree)
467
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
546
        d = get_diff_as_string(tree.basis_tree(), tree)
 
547
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
468
548
        # We shouldn't have a --- or +++ line, because there is no content
469
549
        # change
470
 
        self.assertNotContainsRe(d, '---')
 
550
        self.assertNotContainsRe(d, b'---')
471
551
 
472
552
    def test_renamed_and_modified_file(self):
473
553
        """Test when a file is only renamed."""
474
554
        tree = self.make_branch_and_tree('tree')
475
 
        self.build_tree_contents([('tree/file', 'contents\n')])
476
 
        tree.add(['file'], ['file-id'])
477
 
        tree.commit('one', rev_id='rev-1')
 
555
        self.build_tree_contents([('tree/file', b'contents\n')])
 
556
        tree.add(['file'], [b'file-id'])
 
557
        tree.commit('one', rev_id=b'rev-1')
478
558
 
479
559
        tree.rename_one('file', 'newname')
480
 
        self.build_tree_contents([('tree/newname', 'new contents\n')])
481
 
        d = self.get_diff(tree.basis_tree(), tree)
482
 
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
 
        self.assertContainsRe(d, '--- old/file\t')
484
 
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
485
 
        self.assertContainsRe(d, '-contents\n'
486
 
                                 '\\+new contents\n')
 
560
        self.build_tree_contents([('tree/newname', b'new contents\n')])
 
561
        d = get_diff_as_string(tree.basis_tree(), tree)
 
562
        self.assertContainsRe(d, b"=== renamed file 'file' => 'newname'\n")
 
563
        self.assertContainsRe(d, b'--- old/file\t')
 
564
        self.assertContainsRe(d, b'\\+\\+\\+ new/newname\t')
 
565
        self.assertContainsRe(d, b'-contents\n'
 
566
                                 b'\\+new contents\n')
487
567
 
488
568
 
489
569
    def test_internal_diff_exec_property(self):
490
570
        tree = self.make_branch_and_tree('tree')
491
571
 
492
572
        tt = transform.TreeTransform(tree)
493
 
        tt.new_file('a', tt.root, 'contents\n', 'a-id', True)
494
 
        tt.new_file('b', tt.root, 'contents\n', 'b-id', False)
495
 
        tt.new_file('c', tt.root, 'contents\n', 'c-id', True)
496
 
        tt.new_file('d', tt.root, 'contents\n', 'd-id', False)
497
 
        tt.new_file('e', tt.root, 'contents\n', 'control-e-id', True)
498
 
        tt.new_file('f', tt.root, 'contents\n', 'control-f-id', False)
 
573
        tt.new_file('a', tt.root, [b'contents\n'], b'a-id', True)
 
574
        tt.new_file('b', tt.root, [b'contents\n'], b'b-id', False)
 
575
        tt.new_file('c', tt.root, [b'contents\n'], b'c-id', True)
 
576
        tt.new_file('d', tt.root, [b'contents\n'], b'd-id', False)
 
577
        tt.new_file('e', tt.root, [b'contents\n'], b'control-e-id', True)
 
578
        tt.new_file('f', tt.root, [b'contents\n'], b'control-f-id', False)
499
579
        tt.apply()
500
 
        tree.commit('one', rev_id='rev-1')
 
580
        tree.commit('one', rev_id=b'rev-1')
501
581
 
502
582
        tt = transform.TreeTransform(tree)
503
 
        tt.set_executability(False, tt.trans_id_file_id('a-id'))
504
 
        tt.set_executability(True, tt.trans_id_file_id('b-id'))
505
 
        tt.set_executability(False, tt.trans_id_file_id('c-id'))
506
 
        tt.set_executability(True, tt.trans_id_file_id('d-id'))
 
583
        tt.set_executability(False, tt.trans_id_file_id(b'a-id'))
 
584
        tt.set_executability(True, tt.trans_id_file_id(b'b-id'))
 
585
        tt.set_executability(False, tt.trans_id_file_id(b'c-id'))
 
586
        tt.set_executability(True, tt.trans_id_file_id(b'd-id'))
507
587
        tt.apply()
508
588
        tree.rename_one('c', 'new-c')
509
589
        tree.rename_one('d', 'new-d')
510
590
 
511
 
        d = self.get_diff(tree.basis_tree(), tree)
512
 
 
513
 
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
514
 
                                  ".*\+x to -x.*\)")
515
 
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
516
 
                                  ".*-x to \+x.*\)")
517
 
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
518
 
                                  ".*\+x to -x.*\)")
519
 
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
520
 
                                  ".*-x to \+x.*\)")
521
 
        self.assertNotContainsRe(d, r"file 'e'")
522
 
        self.assertNotContainsRe(d, r"file 'f'")
523
 
 
 
591
        d = get_diff_as_string(tree.basis_tree(), tree)
 
592
 
 
593
        self.assertContainsRe(d, br"file 'a'.*\(properties changed:"
 
594
                                 br".*\+x to -x.*\)")
 
595
        self.assertContainsRe(d, br"file 'b'.*\(properties changed:"
 
596
                                 br".*-x to \+x.*\)")
 
597
        self.assertContainsRe(d, br"file 'c'.*\(properties changed:"
 
598
                                 br".*\+x to -x.*\)")
 
599
        self.assertContainsRe(d, br"file 'd'.*\(properties changed:"
 
600
                                 br".*-x to \+x.*\)")
 
601
        self.assertNotContainsRe(d, br"file 'e'")
 
602
        self.assertNotContainsRe(d, br"file 'f'")
524
603
 
525
604
    def test_binary_unicode_filenames(self):
526
605
        """Test that contents of files are *not* encoded in UTF-8 when there
527
606
        is a binary file in the diff.
528
607
        """
529
608
        # See https://bugs.launchpad.net/bugs/110092.
530
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
609
        self.requireFeature(features.UnicodeFilenameFeature)
531
610
 
532
 
        # This bug isn't triggered with cStringIO.
533
 
        from StringIO import StringIO
534
611
        tree = self.make_branch_and_tree('tree')
535
612
        alpha, omega = u'\u03b1', u'\u03c9'
536
613
        alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
537
614
        self.build_tree_contents(
538
 
            [('tree/' + alpha, chr(0)),
 
615
            [('tree/' + alpha, b'\0'),
539
616
             ('tree/' + omega,
540
 
              ('The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
541
 
        tree.add([alpha], ['file-id'])
542
 
        tree.add([omega], ['file-id-2'])
543
 
        diff_content = StringIO()
 
617
              (b'The %s and the %s\n' % (alpha_utf8, omega_utf8)))])
 
618
        tree.add([alpha], [b'file-id'])
 
619
        tree.add([omega], [b'file-id-2'])
 
620
        diff_content = StubO()
544
621
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
545
 
        d = diff_content.getvalue()
546
 
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
547
 
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
622
        diff_content.check_types(self, bytes)
 
623
        d = b''.join(diff_content.write_record)
 
624
        self.assertContainsRe(d, br"=== added file '%s'" % alpha_utf8)
 
625
        self.assertContainsRe(d, b"Binary files a/%s.*and b/%s.* differ\n"
548
626
                              % (alpha_utf8, alpha_utf8))
549
 
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
550
 
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
551
 
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
 
627
        self.assertContainsRe(d, br"=== added file '%s'" % omega_utf8)
 
628
        self.assertContainsRe(d, br"--- a/%s" % (omega_utf8,))
 
629
        self.assertContainsRe(d, br"\+\+\+ b/%s" % (omega_utf8,))
552
630
 
553
631
    def test_unicode_filename(self):
554
632
        """Test when the filename are unicode."""
555
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
633
        self.requireFeature(features.UnicodeFilenameFeature)
556
634
 
557
635
        alpha, omega = u'\u03b1', u'\u03c9'
558
636
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
559
637
 
560
638
        tree = self.make_branch_and_tree('tree')
561
 
        self.build_tree_contents([('tree/ren_'+alpha, 'contents\n')])
562
 
        tree.add(['ren_'+alpha], ['file-id-2'])
563
 
        self.build_tree_contents([('tree/del_'+alpha, 'contents\n')])
564
 
        tree.add(['del_'+alpha], ['file-id-3'])
565
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents\n')])
566
 
        tree.add(['mod_'+alpha], ['file-id-4'])
 
639
        self.build_tree_contents([('tree/ren_'+alpha, b'contents\n')])
 
640
        tree.add(['ren_'+alpha], [b'file-id-2'])
 
641
        self.build_tree_contents([('tree/del_'+alpha, b'contents\n')])
 
642
        tree.add(['del_'+alpha], [b'file-id-3'])
 
643
        self.build_tree_contents([('tree/mod_'+alpha, b'contents\n')])
 
644
        tree.add(['mod_'+alpha], [b'file-id-4'])
567
645
 
568
 
        tree.commit('one', rev_id='rev-1')
 
646
        tree.commit('one', rev_id=b'rev-1')
569
647
 
570
648
        tree.rename_one('ren_'+alpha, 'ren_'+omega)
571
649
        tree.remove('del_'+alpha)
572
 
        self.build_tree_contents([('tree/add_'+alpha, 'contents\n')])
573
 
        tree.add(['add_'+alpha], ['file-id'])
574
 
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
 
650
        self.build_tree_contents([('tree/add_'+alpha, b'contents\n')])
 
651
        tree.add(['add_'+alpha], [b'file-id'])
 
652
        self.build_tree_contents([('tree/mod_'+alpha, b'contents_mod\n')])
575
653
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
 
654
        d = get_diff_as_string(tree.basis_tree(), tree)
577
655
        self.assertContainsRe(d,
578
 
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
 
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
 
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
 
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
 
656
                b"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
 
657
        self.assertContainsRe(d, b"=== added file 'add_%s'"%autf8)
 
658
        self.assertContainsRe(d, b"=== modified file 'mod_%s'"%autf8)
 
659
        self.assertContainsRe(d, b"=== removed file 'del_%s'"%autf8)
 
660
 
 
661
    def test_unicode_filename_path_encoding(self):
 
662
        """Test for bug #382699: unicode filenames on Windows should be shown
 
663
        in user encoding.
 
664
        """
 
665
        self.requireFeature(features.UnicodeFilenameFeature)
 
666
        # The word 'test' in Russian
 
667
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
668
        directory = _russian_test + u'/'
 
669
        test_txt = _russian_test + u'.txt'
 
670
        u1234 = u'\u1234.txt'
 
671
 
 
672
        tree = self.make_branch_and_tree('.')
 
673
        self.build_tree_contents([
 
674
            (test_txt, b'foo\n'),
 
675
            (u1234, b'foo\n'),
 
676
            (directory, None),
 
677
            ])
 
678
        tree.add([test_txt, u1234, directory])
 
679
 
 
680
        sio = BytesIO()
 
681
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
682
            path_encoding='cp1251')
 
683
 
 
684
        output = subst_dates(sio.getvalue())
 
685
        shouldbe = (b'''\
 
686
=== added directory '%(directory)s'
 
687
=== added file '%(test_txt)s'
 
688
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
689
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
690
@@ -0,0 +1,1 @@
 
691
+foo
 
692
 
 
693
=== added file '?.txt'
 
694
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
695
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
696
@@ -0,0 +1,1 @@
 
697
+foo
 
698
 
 
699
''' % {b'directory': _russian_test.encode('cp1251'),
 
700
       b'test_txt': test_txt.encode('cp1251'),
 
701
      })
 
702
        self.assertEqualDiff(output, shouldbe)
582
703
 
583
704
 
584
705
class DiffWasIs(diff.DiffPath):
585
706
 
586
707
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
587
 
        self.to_file.write('was: ')
588
 
        self.to_file.write(self.old_tree.get_file(file_id).read())
589
 
        self.to_file.write('is: ')
590
 
        self.to_file.write(self.new_tree.get_file(file_id).read())
591
 
        pass
 
708
        self.to_file.write(b'was: ')
 
709
        self.to_file.write(self.old_tree.get_file(old_path).read())
 
710
        self.to_file.write(b'is: ')
 
711
        self.to_file.write(self.new_tree.get_file(new_path).read())
592
712
 
593
713
 
594
714
class TestDiffTree(tests.TestCaseWithTransport):
601
721
        self.new_tree = self.make_branch_and_tree('new-tree')
602
722
        self.new_tree.lock_write()
603
723
        self.addCleanup(self.new_tree.unlock)
604
 
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
724
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
605
725
 
606
726
    def test_diff_text(self):
607
727
        self.build_tree_contents([('old-tree/olddir/',),
608
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
728
                                  ('old-tree/olddir/oldfile', b'old\n')])
609
729
        self.old_tree.add('olddir')
610
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
730
        self.old_tree.add('olddir/oldfile', b'file-id')
611
731
        self.build_tree_contents([('new-tree/newdir/',),
612
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
732
                                  ('new-tree/newdir/newfile', b'new\n')])
613
733
        self.new_tree.add('newdir')
614
 
        self.new_tree.add('newdir/newfile', 'file-id')
615
 
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
616
 
        differ.diff_text('file-id', None, 'old label', 'new label')
617
 
        self.assertEqual(
618
 
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
619
 
            differ.to_file.getvalue())
620
 
        differ.to_file.seek(0)
621
 
        differ.diff_text(None, 'file-id', 'old label', 'new label')
622
 
        self.assertEqual(
623
 
            '--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
624
 
            differ.to_file.getvalue())
625
 
        differ.to_file.seek(0)
626
 
        differ.diff_text('file-id', 'file-id', 'old label', 'new label')
627
 
        self.assertEqual(
628
 
            '--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
 
734
        self.new_tree.add('newdir/newfile', b'file-id')
 
735
        differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
 
736
        differ.diff_text('olddir/oldfile', None, 'old label',
 
737
                         'new label', b'file-id', None)
 
738
        self.assertEqual(
 
739
            b'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
 
740
            differ.to_file.getvalue())
 
741
        differ.to_file.seek(0)
 
742
        differ.diff_text(None, 'newdir/newfile',
 
743
                         'old label', 'new label', None, b'file-id')
 
744
        self.assertEqual(
 
745
            b'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
 
746
            differ.to_file.getvalue())
 
747
        differ.to_file.seek(0)
 
748
        differ.diff_text('olddir/oldfile', 'newdir/newfile',
 
749
                         'old label', 'new label', b'file-id', b'file-id')
 
750
        self.assertEqual(
 
751
            b'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
629
752
            differ.to_file.getvalue())
630
753
 
631
754
    def test_diff_deletion(self):
632
 
        self.build_tree_contents([('old-tree/file', 'contents'),
633
 
                                  ('new-tree/file', 'contents')])
634
 
        self.old_tree.add('file', 'file-id')
635
 
        self.new_tree.add('file', 'file-id')
 
755
        self.build_tree_contents([('old-tree/file', b'contents'),
 
756
                                  ('new-tree/file', b'contents')])
 
757
        self.old_tree.add('file', b'file-id')
 
758
        self.new_tree.add('file', b'file-id')
636
759
        os.unlink('new-tree/file')
637
760
        self.differ.show_diff(None)
638
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '-contents')
 
761
        self.assertContainsRe(self.differ.to_file.getvalue(), b'-contents')
639
762
 
640
763
    def test_diff_creation(self):
641
 
        self.build_tree_contents([('old-tree/file', 'contents'),
642
 
                                  ('new-tree/file', 'contents')])
643
 
        self.old_tree.add('file', 'file-id')
644
 
        self.new_tree.add('file', 'file-id')
 
764
        self.build_tree_contents([('old-tree/file', b'contents'),
 
765
                                  ('new-tree/file', b'contents')])
 
766
        self.old_tree.add('file', b'file-id')
 
767
        self.new_tree.add('file', b'file-id')
645
768
        os.unlink('old-tree/file')
646
769
        self.differ.show_diff(None)
647
 
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
 
770
        self.assertContainsRe(self.differ.to_file.getvalue(), br'\+contents')
648
771
 
649
772
    def test_diff_symlink(self):
650
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
773
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
774
        differ.diff_symlink('old target', None)
652
 
        self.assertEqual("=== target was 'old target'\n",
 
775
        self.assertEqual(b"=== target was 'old target'\n",
653
776
                         differ.to_file.getvalue())
654
777
 
655
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
778
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
779
        differ.diff_symlink(None, 'new target')
657
 
        self.assertEqual("=== target is 'new target'\n",
 
780
        self.assertEqual(b"=== target is 'new target'\n",
658
781
                         differ.to_file.getvalue())
659
782
 
660
 
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
783
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
784
        differ.diff_symlink('old target', 'new target')
662
 
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
 
785
        self.assertEqual(b"=== target changed 'old target' => 'new target'\n",
663
786
                         differ.to_file.getvalue())
664
787
 
665
788
    def test_diff(self):
666
789
        self.build_tree_contents([('old-tree/olddir/',),
667
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
790
                                  ('old-tree/olddir/oldfile', b'old\n')])
668
791
        self.old_tree.add('olddir')
669
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
792
        self.old_tree.add('olddir/oldfile', b'file-id')
670
793
        self.build_tree_contents([('new-tree/newdir/',),
671
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
794
                                  ('new-tree/newdir/newfile', b'new\n')])
672
795
        self.new_tree.add('newdir')
673
 
        self.new_tree.add('newdir/newfile', 'file-id')
674
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
796
        self.new_tree.add('newdir/newfile', b'file-id')
 
797
        self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
675
798
        self.assertContainsRe(
676
799
            self.differ.to_file.getvalue(),
677
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
678
 
             ' \@\@\n-old\n\+new\n\n')
 
800
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
801
            br' \@\@\n-old\n\+new\n\n')
679
802
 
680
803
    def test_diff_kind_change(self):
681
 
        self.requireFeature(tests.SymlinkFeature)
 
804
        self.requireFeature(features.SymlinkFeature)
682
805
        self.build_tree_contents([('old-tree/olddir/',),
683
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
806
                                  ('old-tree/olddir/oldfile', b'old\n')])
684
807
        self.old_tree.add('olddir')
685
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
808
        self.old_tree.add('olddir/oldfile', b'file-id')
686
809
        self.build_tree(['new-tree/newdir/'])
687
810
        os.symlink('new', 'new-tree/newdir/newfile')
688
811
        self.new_tree.add('newdir')
689
 
        self.new_tree.add('newdir/newfile', 'file-id')
690
 
        self.differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
812
        self.new_tree.add('newdir/newfile', b'file-id')
 
813
        self.differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
691
814
        self.assertContainsRe(
692
815
            self.differ.to_file.getvalue(),
693
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
694
 
             ' \@\@\n-old\n\n')
 
816
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
 
817
            br' \@\@\n-old\n\n')
695
818
        self.assertContainsRe(self.differ.to_file.getvalue(),
696
 
                              "=== target is u'new'\n")
 
819
                              b"=== target is 'new'\n")
697
820
 
698
821
    def test_diff_directory(self):
699
822
        self.build_tree(['new-tree/new-dir/'])
700
 
        self.new_tree.add('new-dir', 'new-dir-id')
701
 
        self.differ.diff('new-dir-id', None, 'new-dir')
702
 
        self.assertEqual(self.differ.to_file.getvalue(), '')
 
823
        self.new_tree.add('new-dir', b'new-dir-id')
 
824
        self.differ.diff(b'new-dir-id', None, 'new-dir')
 
825
        self.assertEqual(self.differ.to_file.getvalue(), b'')
703
826
 
704
827
    def create_old_new(self):
705
828
        self.build_tree_contents([('old-tree/olddir/',),
706
 
                                  ('old-tree/olddir/oldfile', 'old\n')])
 
829
                                  ('old-tree/olddir/oldfile', b'old\n')])
707
830
        self.old_tree.add('olddir')
708
 
        self.old_tree.add('olddir/oldfile', 'file-id')
 
831
        self.old_tree.add('olddir/oldfile', b'file-id')
709
832
        self.build_tree_contents([('new-tree/newdir/',),
710
 
                                  ('new-tree/newdir/newfile', 'new\n')])
 
833
                                  ('new-tree/newdir/newfile', b'new\n')])
711
834
        self.new_tree.add('newdir')
712
 
        self.new_tree.add('newdir/newfile', 'file-id')
 
835
        self.new_tree.add('newdir/newfile', b'file-id')
713
836
 
714
837
    def test_register_diff(self):
715
838
        self.create_old_new()
717
840
        diff.DiffTree.diff_factories=old_diff_factories[:]
718
841
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
719
842
        try:
720
 
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
 
843
            differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
721
844
        finally:
722
845
            diff.DiffTree.diff_factories = old_diff_factories
723
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
846
        differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
724
847
        self.assertNotContainsRe(
725
848
            differ.to_file.getvalue(),
726
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
 
             ' \@\@\n-old\n\+new\n\n')
 
849
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
850
            br' \@\@\n-old\n\+new\n\n')
728
851
        self.assertContainsRe(differ.to_file.getvalue(),
729
 
                              'was: old\nis: new\n')
 
852
                              b'was: old\nis: new\n')
730
853
 
731
854
    def test_extra_factories(self):
732
855
        self.create_old_new()
733
 
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
856
        differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
734
857
                               extra_factories=[DiffWasIs.from_diff_tree])
735
 
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
 
858
        differ.diff(b'file-id', 'olddir/oldfile', 'newdir/newfile')
736
859
        self.assertNotContainsRe(
737
860
            differ.to_file.getvalue(),
738
 
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
 
             ' \@\@\n-old\n\+new\n\n')
 
861
            br'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
 
862
            br' \@\@\n-old\n\+new\n\n')
740
863
        self.assertContainsRe(differ.to_file.getvalue(),
741
 
                              'was: old\nis: new\n')
 
864
                              b'was: old\nis: new\n')
742
865
 
743
866
    def test_alphabetical_order(self):
744
867
        self.build_tree(['new-tree/a-file'])
747
870
        self.old_tree.add('b-file')
748
871
        self.differ.show_diff(None)
749
872
        self.assertContainsRe(self.differ.to_file.getvalue(),
750
 
            '.*a-file(.|\n)*b-file')
 
873
            b'.*a-file(.|\n)*b-file')
751
874
 
752
875
 
753
876
class TestPatienceDiffLib(tests.TestCase):
764
887
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
888
        sm = self._PatienceSequenceMatcher(None, a, b)
766
889
        mb = sm.get_matching_blocks()
767
 
        self.assertEquals(35, len(mb))
 
890
        self.assertEqual(35, len(mb))
768
891
 
769
892
    def test_unique_lcs(self):
770
893
        unique_lcs = self._unique_lcs
771
 
        self.assertEquals(unique_lcs('', ''), [])
772
 
        self.assertEquals(unique_lcs('', 'a'), [])
773
 
        self.assertEquals(unique_lcs('a', ''), [])
774
 
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
775
 
        self.assertEquals(unique_lcs('a', 'b'), [])
776
 
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
777
 
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
778
 
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
779
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
780
 
                                                         (3,3), (4,4)])
781
 
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
 
894
        self.assertEqual(unique_lcs('', ''), [])
 
895
        self.assertEqual(unique_lcs('', 'a'), [])
 
896
        self.assertEqual(unique_lcs('a', ''), [])
 
897
        self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
 
898
        self.assertEqual(unique_lcs('a', 'b'), [])
 
899
        self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
 
900
        self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
 
901
        self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
 
902
        self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
 
903
                                                         (3, 3), (4, 4)])
 
904
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
782
905
 
783
906
    def test_recurse_matches(self):
784
907
        def test_one(a, b, matches):
785
908
            test_matches = []
786
909
            self._recurse_matches(
787
910
                a, b, 0, 0, len(a), len(b), test_matches, 10)
788
 
            self.assertEquals(test_matches, matches)
 
911
            self.assertEqual(test_matches, matches)
789
912
 
790
913
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
914
                 [(0, 0), (2, 2), (4, 4)])
794
917
        # Even though 'bc' is not unique globally, and is surrounded by
795
918
        # non-matching lines, we should still match, because they are locally
796
919
        # unique
797
 
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
 
920
        test_one('abcdbce', 'afbcgdbce', [(0, 0), (1, 2), (2, 3), (3, 5),
798
921
                                          (4, 6), (5, 7), (6, 8)])
799
922
 
800
923
        # recurse_matches doesn't match non-unique
805
928
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
806
929
 
807
930
        # This is what it currently gives:
808
 
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
 
931
        test_one('aBccDe', 'abccde', [(0, 0), (5, 5)])
809
932
 
810
933
    def assertDiffBlocks(self, a, b, expected_blocks):
811
934
        """Check that the sequence matcher returns the correct blocks.
853
976
 
854
977
        # non unique lines surrounded by non-matching lines
855
978
        # won't be found
856
 
        self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
 
979
        self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
857
980
 
858
981
        # But they only need to be locally unique
859
 
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
 
982
        self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
860
983
 
861
984
        # non unique blocks won't be matched
862
 
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
 
985
        self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
863
986
 
864
987
        # but locally unique ones will
865
 
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
866
 
                                              (5,4,1), (7,5,2), (10,8,1)])
 
988
        self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
 
989
                                              (5, 4, 1), (7, 5, 2), (10, 8, 1)])
867
990
 
868
 
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
 
991
        self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
869
992
        self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
870
993
        self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
871
994
 
896
1019
    def test_opcodes(self):
897
1020
        def chk_ops(a, b, expected_codes):
898
1021
            s = self._PatienceSequenceMatcher(None, a, b)
899
 
            self.assertEquals(expected_codes, s.get_opcodes())
 
1022
            self.assertEqual(expected_codes, s.get_opcodes())
900
1023
 
901
1024
        chk_ops('', '', [])
902
1025
        chk_ops([], [], [])
903
 
        chk_ops('abc', '', [('delete', 0,3, 0,0)])
904
 
        chk_ops('', 'abc', [('insert', 0,0, 0,3)])
905
 
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
906
 
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
907
 
                                 ('replace', 3,4, 3,4)
908
 
                                ])
909
 
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
910
 
                                 ('equal',  1,4, 0,3),
911
 
                                 ('insert', 4,4, 3,4)
912
 
                                ])
913
 
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
914
 
                                  ('equal',  1,5, 0,4)
 
1026
        chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
 
1027
        chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
 
1028
        chk_ops('abcd', 'abcd', [('equal',    0, 4, 0, 4)])
 
1029
        chk_ops('abcd', 'abce', [('equal',   0, 3, 0, 3),
 
1030
                                 ('replace', 3, 4, 3, 4)
 
1031
                                ])
 
1032
        chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
 
1033
                                 ('equal',  1, 4, 0, 3),
 
1034
                                 ('insert', 4, 4, 3, 4)
 
1035
                                ])
 
1036
        chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
 
1037
                                  ('equal',  1, 5, 0, 4)
915
1038
                                 ])
916
 
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
917
 
                                   ('replace', 2,3, 2,3),
918
 
                                   ('equal',   3,5, 3,5)
 
1039
        chk_ops('abcde', 'abXde', [('equal',   0, 2, 0, 2),
 
1040
                                   ('replace', 2, 3, 2, 3),
 
1041
                                   ('equal',   3, 5, 3, 5)
919
1042
                                  ])
920
 
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
921
 
                                     ('replace', 2,3, 2,5),
922
 
                                     ('equal',   3,5, 5,7)
 
1043
        chk_ops('abcde', 'abXYZde', [('equal',   0, 2, 0, 2),
 
1044
                                     ('replace', 2, 3, 2, 5),
 
1045
                                     ('equal',   3, 5, 5, 7)
923
1046
                                    ])
924
 
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
925
 
                                    ('insert', 2,2, 2,5),
926
 
                                    ('equal',  2,4, 5,7)
 
1047
        chk_ops('abde', 'abXYZde', [('equal',  0, 2, 0, 2),
 
1048
                                    ('insert', 2, 2, 2, 5),
 
1049
                                    ('equal',  2, 4, 5, 7)
927
1050
                                   ])
928
1051
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
 
                [('equal',  0,6,  0,6),
930
 
                 ('insert', 6,6,  6,11),
931
 
                 ('equal',  6,16, 11,21)
 
1052
                [('equal',  0, 6,  0, 6),
 
1053
                 ('insert', 6, 6,  6, 11),
 
1054
                 ('equal',  6, 16, 11, 21)
932
1055
                ])
933
1056
        chk_ops(
934
1057
                [ 'hello there\n'
936
1059
                , 'how are you today?\n'],
937
1060
                [ 'hello there\n'
938
1061
                , 'how are you today?\n'],
939
 
                [('equal',  0,1, 0,1),
940
 
                 ('delete', 1,2, 1,1),
941
 
                 ('equal',  2,3, 1,2),
 
1062
                [('equal',  0, 1, 0, 1),
 
1063
                 ('delete', 1, 2, 1, 1),
 
1064
                 ('equal',  2, 3, 1, 2),
942
1065
                ])
943
1066
        chk_ops('aBccDe', 'abccde',
944
 
                [('equal',   0,1, 0,1),
945
 
                 ('replace', 1,5, 1,5),
946
 
                 ('equal',   5,6, 5,6),
 
1067
                [('equal',   0, 1, 0, 1),
 
1068
                 ('replace', 1, 5, 1, 5),
 
1069
                 ('equal',   5, 6, 5, 6),
947
1070
                ])
948
1071
        chk_ops('aBcDec', 'abcdec',
949
 
                [('equal',   0,1, 0,1),
950
 
                 ('replace', 1,2, 1,2),
951
 
                 ('equal',   2,3, 2,3),
952
 
                 ('replace', 3,4, 3,4),
953
 
                 ('equal',   4,6, 4,6),
 
1072
                [('equal',   0, 1, 0, 1),
 
1073
                 ('replace', 1, 2, 1, 2),
 
1074
                 ('equal',   2, 3, 2, 3),
 
1075
                 ('replace', 3, 4, 3, 4),
 
1076
                 ('equal',   4, 6, 4, 6),
954
1077
                ])
955
1078
        chk_ops('aBcdEcdFg', 'abcdecdfg',
956
 
                [('equal',   0,1, 0,1),
957
 
                 ('replace', 1,8, 1,8),
958
 
                 ('equal',   8,9, 8,9)
 
1079
                [('equal',   0, 1, 0, 1),
 
1080
                 ('replace', 1, 8, 1, 8),
 
1081
                 ('equal',   8, 9, 8, 9)
959
1082
                ])
960
1083
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
961
 
                [('equal',   0,1, 0,1),
962
 
                 ('replace', 1,2, 1,2),
963
 
                 ('equal',   2,4, 2,4),
964
 
                 ('delete', 4,5, 4,4),
965
 
                 ('equal',   5,6, 4,5),
966
 
                 ('delete', 6,7, 5,5),
967
 
                 ('equal',   7,9, 5,7),
968
 
                 ('replace', 9,10, 7,8),
969
 
                 ('equal',   10,11, 8,9)
 
1084
                [('equal',   0, 1, 0, 1),
 
1085
                 ('replace', 1, 2, 1, 2),
 
1086
                 ('equal',   2, 4, 2, 4),
 
1087
                 ('delete', 4, 5, 4, 4),
 
1088
                 ('equal',   5, 6, 4, 5),
 
1089
                 ('delete', 6, 7, 5, 5),
 
1090
                 ('equal',   7, 9, 5, 7),
 
1091
                 ('replace', 9, 10, 7, 8),
 
1092
                 ('equal',   10, 11, 8, 9)
970
1093
                ])
971
1094
 
972
1095
    def test_grouped_opcodes(self):
973
1096
        def chk_ops(a, b, expected_codes, n=3):
974
1097
            s = self._PatienceSequenceMatcher(None, a, b)
975
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
 
1098
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
976
1099
 
977
1100
        chk_ops('', '', [])
978
1101
        chk_ops([], [], [])
979
 
        chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
980
 
        chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
 
1102
        chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
 
1103
        chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
981
1104
        chk_ops('abcd', 'abcd', [])
982
 
        chk_ops('abcd', 'abce', [[('equal',   0,3, 0,3),
983
 
                                  ('replace', 3,4, 3,4)
 
1105
        chk_ops('abcd', 'abce', [[('equal',   0, 3, 0, 3),
 
1106
                                  ('replace', 3, 4, 3, 4)
984
1107
                                 ]])
985
 
        chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
986
 
                                 ('equal',  1,4, 0,3),
987
 
                                 ('insert', 4,4, 3,4)
 
1108
        chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
 
1109
                                 ('equal',  1, 4, 0, 3),
 
1110
                                 ('insert', 4, 4, 3, 4)
988
1111
                                ]])
989
1112
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
990
 
                [[('equal',  3,6, 3,6),
991
 
                  ('insert', 6,6, 6,11),
992
 
                  ('equal',  6,9, 11,14)
 
1113
                [[('equal',  3, 6, 3, 6),
 
1114
                  ('insert', 6, 6, 6, 11),
 
1115
                  ('equal',  6, 9, 11, 14)
993
1116
                  ]])
994
1117
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
995
 
                [[('equal',  2,6, 2,6),
996
 
                  ('insert', 6,6, 6,11),
997
 
                  ('equal',  6,10, 11,15)
 
1118
                [[('equal',  2, 6, 2, 6),
 
1119
                  ('insert', 6, 6, 6, 11),
 
1120
                  ('equal',  6, 10, 11, 15)
998
1121
                  ]], 4)
999
1122
        chk_ops('Xabcdef', 'abcdef',
1000
 
                [[('delete', 0,1, 0,0),
1001
 
                  ('equal',  1,4, 0,3)
 
1123
                [[('delete', 0, 1, 0, 0),
 
1124
                  ('equal',  1, 4, 0, 3)
1002
1125
                  ]])
1003
1126
        chk_ops('abcdef', 'abcdefX',
1004
 
                [[('equal',  3,6, 3,6),
1005
 
                  ('insert', 6,6, 6,7)
 
1127
                [[('equal',  3, 6, 3, 6),
 
1128
                  ('insert', 6, 6, 6, 7)
1006
1129
                  ]])
1007
1130
 
1008
1131
 
1015
1138
 
1016
1139
        self.assertDiffBlocks('ABCd efghIjk  L',
1017
1140
                              'AxyzBCn mo pqrstuvwI1 2  L',
1018
 
                              [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1141
                              [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1019
1142
 
1020
1143
        # These are rot13 code snippets.
1021
1144
        self.assertDiffBlocks('''\
1062
1185
 
1063
1186
pynff pzq_zxqve(Pbzznaq):
1064
1187
'''.splitlines(True)
1065
 
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
1188
, [(0, 0, 1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
1066
1189
 
1067
1190
    def test_patience_unified_diff(self):
1068
1191
        txt_a = ['hello there\n',
1072
1195
                 'how are you today?\n']
1073
1196
        unified_diff = patiencediff.unified_diff
1074
1197
        psm = self._PatienceSequenceMatcher
1075
 
        self.assertEquals(['--- \n',
 
1198
        self.assertEqual(['--- \n',
1076
1199
                           '+++ \n',
1077
1200
                           '@@ -1,3 +1,2 @@\n',
1078
1201
                           ' hello there\n',
1081
1204
                          ]
1082
1205
                          , list(unified_diff(txt_a, txt_b,
1083
1206
                                 sequencematcher=psm)))
1084
 
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1085
 
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
 
1207
        txt_a = [x+'\n' for x in 'abcdefghijklmnop']
 
1208
        txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
1086
1209
        # This is the result with LongestCommonSubstring matching
1087
 
        self.assertEquals(['--- \n',
 
1210
        self.assertEqual(['--- \n',
1088
1211
                           '+++ \n',
1089
1212
                           '@@ -1,6 +1,11 @@\n',
1090
1213
                           ' a\n',
1100
1223
                           ' f\n']
1101
1224
                          , list(unified_diff(txt_a, txt_b)))
1102
1225
        # And the patience diff
1103
 
        self.assertEquals(['--- \n',
 
1226
        self.assertEqual(['--- \n',
1104
1227
                           '+++ \n',
1105
1228
                           '@@ -4,6 +4,11 @@\n',
1106
1229
                           ' d\n',
1126
1249
                 'how are you today?\n']
1127
1250
        unified_diff = patiencediff.unified_diff
1128
1251
        psm = self._PatienceSequenceMatcher
1129
 
        self.assertEquals(['--- a\t2008-08-08\n',
 
1252
        self.assertEqual(['--- a\t2008-08-08\n',
1130
1253
                           '+++ b\t2008-09-09\n',
1131
1254
                           '@@ -1,3 +1,2 @@\n',
1132
1255
                           ' hello there\n',
1142
1265
 
1143
1266
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1144
1267
 
1145
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1268
    _test_needs_features = [features.compiled_patiencediff_feature]
1146
1269
 
1147
1270
    def setUp(self):
1148
1271
        super(TestPatienceDiffLib_c, self).setUp()
1149
 
        from bzrlib import _patiencediff_c
 
1272
        from breezy import _patiencediff_c
1150
1273
        self._unique_lcs = _patiencediff_c.unique_lcs_c
1151
1274
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1152
1275
        self._PatienceSequenceMatcher = \
1174
1297
            _patiencediff_py.PatienceSequenceMatcher_py
1175
1298
 
1176
1299
    def test_patience_unified_diff_files(self):
1177
 
        txt_a = ['hello there\n',
1178
 
                 'world\n',
1179
 
                 'how are you today?\n']
1180
 
        txt_b = ['hello there\n',
1181
 
                 'how are you today?\n']
1182
 
        open('a1', 'wb').writelines(txt_a)
1183
 
        open('b1', 'wb').writelines(txt_b)
 
1300
        txt_a = [b'hello there\n',
 
1301
                 b'world\n',
 
1302
                 b'how are you today?\n']
 
1303
        txt_b = [b'hello there\n',
 
1304
                 b'how are you today?\n']
 
1305
        with open('a1', 'wb') as f: f.writelines(txt_a)
 
1306
        with open('b1', 'wb') as f: f.writelines(txt_b)
1184
1307
 
1185
1308
        unified_diff_files = patiencediff.unified_diff_files
1186
1309
        psm = self._PatienceSequenceMatcher
1187
 
        self.assertEquals(['--- a1\n',
1188
 
                           '+++ b1\n',
1189
 
                           '@@ -1,3 +1,2 @@\n',
1190
 
                           ' hello there\n',
1191
 
                           '-world\n',
1192
 
                           ' how are you today?\n',
 
1310
        self.assertEqual([b'--- a1\n',
 
1311
                          b'+++ b1\n',
 
1312
                          b'@@ -1,3 +1,2 @@\n',
 
1313
                          b' hello there\n',
 
1314
                          b'-world\n',
 
1315
                          b' how are you today?\n',
1193
1316
                          ]
1194
 
                          , list(unified_diff_files('a1', 'b1',
 
1317
                          , list(unified_diff_files(b'a1', b'b1',
1195
1318
                                 sequencematcher=psm)))
1196
1319
 
1197
 
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1198
 
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1199
 
        open('a2', 'wb').writelines(txt_a)
1200
 
        open('b2', 'wb').writelines(txt_b)
 
1320
        txt_a = [x+'\n' for x in 'abcdefghijklmnop']
 
1321
        txt_b = [x+'\n' for x in 'abcdefxydefghijklmnop']
 
1322
        with open('a2', 'wt') as f: f.writelines(txt_a)
 
1323
        with open('b2', 'wt') as f: f.writelines(txt_b)
1201
1324
 
1202
1325
        # This is the result with LongestCommonSubstring matching
1203
 
        self.assertEquals(['--- a2\n',
1204
 
                           '+++ b2\n',
1205
 
                           '@@ -1,6 +1,11 @@\n',
1206
 
                           ' a\n',
1207
 
                           ' b\n',
1208
 
                           ' c\n',
1209
 
                           '+d\n',
1210
 
                           '+e\n',
1211
 
                           '+f\n',
1212
 
                           '+x\n',
1213
 
                           '+y\n',
1214
 
                           ' d\n',
1215
 
                           ' e\n',
1216
 
                           ' f\n']
1217
 
                          , list(unified_diff_files('a2', 'b2')))
 
1326
        self.assertEqual([b'--- a2\n',
 
1327
                          b'+++ b2\n',
 
1328
                          b'@@ -1,6 +1,11 @@\n',
 
1329
                          b' a\n',
 
1330
                          b' b\n',
 
1331
                          b' c\n',
 
1332
                          b'+d\n',
 
1333
                          b'+e\n',
 
1334
                          b'+f\n',
 
1335
                          b'+x\n',
 
1336
                          b'+y\n',
 
1337
                          b' d\n',
 
1338
                          b' e\n',
 
1339
                          b' f\n']
 
1340
                          , list(unified_diff_files(b'a2', b'b2')))
1218
1341
 
1219
1342
        # And the patience diff
1220
 
        self.assertEquals(['--- a2\n',
1221
 
                           '+++ b2\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_files('a2', 'b2',
1236
 
                                 sequencematcher=psm)))
 
1343
        self.assertEqual([b'--- a2\n',
 
1344
                          b'+++ b2\n',
 
1345
                          b'@@ -4,6 +4,11 @@\n',
 
1346
                          b' d\n',
 
1347
                          b' e\n',
 
1348
                          b' f\n',
 
1349
                          b'+x\n',
 
1350
                          b'+y\n',
 
1351
                          b'+d\n',
 
1352
                          b'+e\n',
 
1353
                          b'+f\n',
 
1354
                          b' g\n',
 
1355
                          b' h\n',
 
1356
                          b' i\n'],
 
1357
                         list(unified_diff_files(b'a2', b'b2',
 
1358
                                                 sequencematcher=psm)))
1237
1359
 
1238
1360
 
1239
1361
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1240
1362
 
1241
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1363
    _test_needs_features = [features.compiled_patiencediff_feature]
1242
1364
 
1243
1365
    def setUp(self):
1244
1366
        super(TestPatienceDiffLibFiles_c, self).setUp()
1245
 
        from bzrlib import _patiencediff_c
 
1367
        from breezy import _patiencediff_c
1246
1368
        self._PatienceSequenceMatcher = \
1247
1369
            _patiencediff_c.PatienceSequenceMatcher_c
1248
1370
 
1250
1372
class TestUsingCompiledIfAvailable(tests.TestCase):
1251
1373
 
1252
1374
    def test_PatienceSequenceMatcher(self):
1253
 
        if compiled_patiencediff_feature.available():
1254
 
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
 
1375
        if features.compiled_patiencediff_feature.available():
 
1376
            from breezy._patiencediff_c import PatienceSequenceMatcher_c
1255
1377
            self.assertIs(PatienceSequenceMatcher_c,
1256
1378
                          patiencediff.PatienceSequenceMatcher)
1257
1379
        else:
1258
 
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
 
1380
            from breezy._patiencediff_py import PatienceSequenceMatcher_py
1259
1381
            self.assertIs(PatienceSequenceMatcher_py,
1260
1382
                          patiencediff.PatienceSequenceMatcher)
1261
1383
 
1262
1384
    def test_unique_lcs(self):
1263
 
        if compiled_patiencediff_feature.available():
1264
 
            from bzrlib._patiencediff_c import unique_lcs_c
 
1385
        if features.compiled_patiencediff_feature.available():
 
1386
            from breezy._patiencediff_c import unique_lcs_c
1265
1387
            self.assertIs(unique_lcs_c,
1266
1388
                          patiencediff.unique_lcs)
1267
1389
        else:
1268
 
            from bzrlib._patiencediff_py import unique_lcs_py
 
1390
            from breezy._patiencediff_py import unique_lcs_py
1269
1391
            self.assertIs(unique_lcs_py,
1270
1392
                          patiencediff.unique_lcs)
1271
1393
 
1272
1394
    def test_recurse_matches(self):
1273
 
        if compiled_patiencediff_feature.available():
1274
 
            from bzrlib._patiencediff_c import recurse_matches_c
 
1395
        if features.compiled_patiencediff_feature.available():
 
1396
            from breezy._patiencediff_c import recurse_matches_c
1275
1397
            self.assertIs(recurse_matches_c,
1276
1398
                          patiencediff.recurse_matches)
1277
1399
        else:
1278
 
            from bzrlib._patiencediff_py import recurse_matches_py
 
1400
            from breezy._patiencediff_py import recurse_matches_py
1279
1401
            self.assertIs(recurse_matches_py,
1280
1402
                          patiencediff.recurse_matches)
1281
1403
 
1298
1420
                         diff_obj._get_command('old-path', 'new-path'))
1299
1421
 
1300
1422
    def test_from_string_path_with_backslashes(self):
1301
 
        self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
 
1423
        self.requireFeature(features.backslashdir_feature)
1302
1424
        tool = 'C:\\Tools\\Diff.exe'
1303
1425
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1304
1426
        self.addCleanup(diff_obj.finish)
1308
1430
                         diff_obj._get_command('old-path', 'new-path'))
1309
1431
 
1310
1432
    def test_execute(self):
1311
 
        output = StringIO()
1312
 
        diff_obj = diff.DiffFromTool(['python', '-c',
1313
 
                                      'print "@old_path @new_path"'],
 
1433
        output = BytesIO()
 
1434
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
 
1435
                                      'print("@old_path @new_path")'],
1314
1436
                                     None, None, output)
1315
1437
        self.addCleanup(diff_obj.finish)
1316
1438
        diff_obj._execute('old', 'new')
1317
 
        self.assertEqual(output.getvalue().rstrip(), 'old new')
 
1439
        self.assertEqual(output.getvalue().rstrip(), b'old new')
1318
1440
 
1319
 
    def test_excute_missing(self):
 
1441
    def test_execute_missing(self):
1320
1442
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
1443
                                     None, None, None)
1322
1444
        self.addCleanup(diff_obj.finish)
1326
1448
                         ' on this machine', str(e))
1327
1449
 
1328
1450
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1329
 
        self.requireFeature(AttribFeature)
1330
 
        output = StringIO()
 
1451
        self.requireFeature(features.AttribFeature)
 
1452
        output = BytesIO()
1331
1453
        tree = self.make_branch_and_tree('tree')
1332
 
        self.build_tree_contents([('tree/file', 'content')])
1333
 
        tree.add('file', 'file-id')
 
1454
        self.build_tree_contents([('tree/file', b'content')])
 
1455
        tree.add('file', b'file-id')
1334
1456
        tree.commit('old tree')
1335
1457
        tree.lock_read()
1336
1458
        self.addCleanup(tree.unlock)
1337
1459
        basis_tree = tree.basis_tree()
1338
1460
        basis_tree.lock_read()
1339
1461
        self.addCleanup(basis_tree.unlock)
1340
 
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1462
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
1341
1463
                                      'print "@old_path @new_path"'],
1342
1464
                                     basis_tree, tree, output)
1343
 
        diff_obj._prepare_files('file-id', 'file', 'file')
 
1465
        diff_obj._prepare_files('file', 'file', file_id=b'file-id')
1344
1466
        # The old content should be readonly
1345
1467
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
1346
1468
                                    r'R.*old\\file$')
1356
1478
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1357
1479
 
1358
1480
    def test_prepare_files(self):
1359
 
        output = StringIO()
 
1481
        output = BytesIO()
1360
1482
        tree = self.make_branch_and_tree('tree')
1361
 
        self.build_tree_contents([('tree/oldname', 'oldcontent')])
1362
 
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1363
 
        tree.add('oldname', 'file-id')
1364
 
        tree.add('oldname2', 'file2-id')
 
1483
        self.build_tree_contents([('tree/oldname', b'oldcontent')])
 
1484
        self.build_tree_contents([('tree/oldname2', b'oldcontent2')])
 
1485
        tree.add('oldname', b'file-id')
 
1486
        tree.add('oldname2', b'file2-id')
1365
1487
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
1366
1488
        tree.commit('old tree', timestamp=315532800)
1367
1489
        tree.rename_one('oldname', 'newname')
1368
1490
        tree.rename_one('oldname2', 'newname2')
1369
 
        self.build_tree_contents([('tree/newname', 'newcontent')])
1370
 
        self.build_tree_contents([('tree/newname2', 'newcontent2')])
 
1491
        self.build_tree_contents([('tree/newname', b'newcontent')])
 
1492
        self.build_tree_contents([('tree/newname2', b'newcontent2')])
1371
1493
        old_tree = tree.basis_tree()
1372
1494
        old_tree.lock_read()
1373
1495
        self.addCleanup(old_tree.unlock)
1374
1496
        tree.lock_read()
1375
1497
        self.addCleanup(tree.unlock)
1376
 
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1498
        diff_obj = diff.DiffFromTool([sys.executable, '-c',
1377
1499
                                      'print "@old_path @new_path"'],
1378
1500
                                     old_tree, tree, output)
1379
1501
        self.addCleanup(diff_obj.finish)
1380
 
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1381
 
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1382
 
                                                     'newname')
 
1502
        self.assertContainsRe(diff_obj._root, 'brz-diff-[^/]*')
 
1503
        old_path, new_path = diff_obj._prepare_files(
 
1504
                'oldname', 'newname', file_id=b'file-id')
1383
1505
        self.assertContainsRe(old_path, 'old/oldname$')
1384
1506
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
1385
1507
        self.assertContainsRe(new_path, 'tree/newname$')
1386
 
        self.assertFileEqual('oldcontent', old_path)
1387
 
        self.assertFileEqual('newcontent', new_path)
 
1508
        self.assertFileEqual(b'oldcontent', old_path)
 
1509
        self.assertFileEqual(b'newcontent', new_path)
1388
1510
        if osutils.host_os_dereferences_symlinks():
1389
1511
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
1512
        # make sure we can create files with the same parent directories
1391
 
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
 
1513
        diff_obj._prepare_files('oldname2', 'newname2', file_id=b'file2-id')
 
1514
 
 
1515
 
 
1516
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1517
 
 
1518
    def test_encodable_filename(self):
 
1519
        # Just checks file path for external diff tool.
 
1520
        # We cannot change CPython's internal encoding used by os.exec*.
 
1521
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1522
                                    None, None, None)
 
1523
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1524
            encoding = scenario['encoding']
 
1525
            dirname = scenario['info']['directory']
 
1526
            filename = scenario['info']['filename']
 
1527
 
 
1528
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1529
            relpath = dirname + u'/' + filename
 
1530
            fullpath = diffobj._safe_filename('safe', relpath)
 
1531
            self.assertEqual(fullpath,
 
1532
                             fullpath.encode(encoding).decode(encoding))
 
1533
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
 
1534
 
 
1535
    def test_unencodable_filename(self):
 
1536
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1537
                                    None, None, None)
 
1538
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1539
            encoding = scenario['encoding']
 
1540
            dirname = scenario['info']['directory']
 
1541
            filename = scenario['info']['filename']
 
1542
 
 
1543
            if encoding == 'iso-8859-1':
 
1544
                encoding = 'iso-8859-2'
 
1545
            else:
 
1546
                encoding = 'iso-8859-1'
 
1547
 
 
1548
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1549
            relpath = dirname + u'/' + filename
 
1550
            fullpath = diffobj._safe_filename('safe', relpath)
 
1551
            self.assertEqual(fullpath,
 
1552
                             fullpath.encode(encoding).decode(encoding))
 
1553
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1392
1554
 
1393
1555
 
1394
1556
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1395
1557
 
1396
1558
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1397
 
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
1398
 
        TestGetTreesAndBranchesToDiff.
1399
 
        """
 
1559
        """Call get_trees_and_branches_to_diff_locked."""
1400
1560
        return diff.get_trees_and_branches_to_diff_locked(
1401
1561
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1402
1562
 
1418
1578
 
1419
1579
    def test_with_rev_specs(self):
1420
1580
        tree = self.make_branch_and_tree('tree')
1421
 
        self.build_tree_contents([('tree/file', 'oldcontent')])
1422
 
        tree.add('file', 'file-id')
1423
 
        tree.commit('old tree', timestamp=0, rev_id="old-id")
1424
 
        self.build_tree_contents([('tree/file', 'newcontent')])
1425
 
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1581
        self.build_tree_contents([('tree/file', b'oldcontent')])
 
1582
        tree.add('file', b'file-id')
 
1583
        tree.commit('old tree', timestamp=0, rev_id=b"old-id")
 
1584
        self.build_tree_contents([('tree/file', b'newcontent')])
 
1585
        tree.commit('new tree', timestamp=0, rev_id=b"new-id")
1426
1586
 
1427
1587
        revisions = [revisionspec.RevisionSpec.from_string('1'),
1428
1588
                     revisionspec.RevisionSpec.from_string('2')]
1432
1592
            ['tree'], revisions, None, None)
1433
1593
 
1434
1594
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
1435
 
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1595
        self.assertEqual(b"old-id", old_tree.get_revision_id())
1436
1596
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
1437
 
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1597
        self.assertEqual(b"new-id", new_tree.get_revision_id())
1438
1598
        self.assertEqual(tree.branch.base, old_branch.base)
1439
1599
        self.assertEqual(tree.branch.base, new_branch.base)
1440
1600
        self.assertIs(None, specific_files)
1441
1601
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
1442
 
 
1443
 
 
1444
 
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
1445
 
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
1446
 
    deprecated get_trees_and_branches_to_diff function.
1447
 
    """
1448
 
 
1449
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1450
 
        return self.applyDeprecated(
1451
 
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
1452
 
            path_list, revision_specs, old_url, new_url)
1453