/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/tests/test_diff.py

  • Committer: Jelmer Vernooij
  • Date: 2018-08-14 01:15:02 UTC
  • mto: This revision was merged to the branch mainline in revision 7078.
  • Revision ID: jelmer@jelmer.uk-20180814011502-5zaydaq02vc2qxo1
Fix tests.

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