~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 05:06:57 UTC
  • mfrom: (6603.4.1 bzr)
  • Revision ID: pqm@pqm.ubuntu.com-20160421050657-ygnzfybewvudf1j9
(richard-wilbur) Use initial_comment as commit_message for lp_propose.(Shawn
 Wang) (Shawn Wang)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2014, 2016 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import os.path
19
18
from cStringIO import StringIO
20
 
import errno
21
19
import subprocess
22
 
import sys
23
 
from tempfile import TemporaryFile
 
20
import tempfile
24
21
 
25
 
from bzrlib import tests
26
 
from bzrlib.diff import (
27
 
    DiffFromTool,
28
 
    DiffPath,
29
 
    DiffSymlink,
30
 
    DiffTree,
31
 
    DiffText,
32
 
    external_diff,
33
 
    internal_diff,
34
 
    show_diff_trees,
 
22
from bzrlib import (
 
23
    diff,
 
24
    errors,
 
25
    osutils,
 
26
    patiencediff,
 
27
    _patiencediff_py,
 
28
    revision as _mod_revision,
 
29
    revisionspec,
 
30
    revisiontree,
 
31
    tests,
 
32
    transform,
35
33
    )
36
 
from bzrlib.errors import BinaryFile, NoDiff, ExecutableMissing
37
 
import bzrlib.osutils as osutils
38
 
import bzrlib.transform as transform
39
 
import bzrlib.patiencediff
40
 
import bzrlib._patiencediff_py
41
 
from bzrlib.tests import (Feature, TestCase, TestCaseWithTransport,
42
 
                          TestCaseInTempDir, TestSkipped)
43
 
 
44
 
 
45
 
class _AttribFeature(Feature):
46
 
 
47
 
    def _probe(self):
48
 
        if (sys.platform not in ('cygwin', 'win32')):
49
 
            return False
50
 
        try:
51
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
52
 
        except OSError, e:
53
 
            return False
54
 
        return (0 == proc.wait())
55
 
 
56
 
    def feature_name(self):
57
 
        return 'attrib Windows command-line tool'
58
 
 
59
 
AttribFeature = _AttribFeature()
60
 
 
61
 
 
62
 
class _CompiledPatienceDiffFeature(Feature):
63
 
 
64
 
    def _probe(self):
65
 
        try:
66
 
            import bzrlib._patiencediff_c
67
 
        except ImportError:
68
 
            return False
69
 
        return True
70
 
 
71
 
    def feature_name(self):
72
 
        return 'bzrlib._patiencediff_c'
73
 
 
74
 
CompiledPatienceDiffFeature = _CompiledPatienceDiffFeature()
 
34
from bzrlib.tests import (
 
35
    features,
 
36
    EncodingAdapter,
 
37
)
 
38
from bzrlib.tests.blackbox.test_diff import subst_dates
 
39
from bzrlib.tests.scenarios import load_tests_apply_scenarios
 
40
 
 
41
 
 
42
load_tests = load_tests_apply_scenarios
75
43
 
76
44
 
77
45
def udiff_lines(old, new, allow_binary=False):
78
46
    output = StringIO()
79
 
    internal_diff('old', old, 'new', new, output, allow_binary)
 
47
    diff.internal_diff('old', old, 'new', new, output, allow_binary)
80
48
    output.seek(0, 0)
81
49
    return output.readlines()
82
50
 
86
54
        # StringIO has no fileno, so it tests a different codepath
87
55
        output = StringIO()
88
56
    else:
89
 
        output = TemporaryFile()
 
57
        output = tempfile.TemporaryFile()
90
58
    try:
91
 
        external_diff('old', old, 'new', new, output, diff_opts=['-u'])
92
 
    except NoDiff:
93
 
        raise TestSkipped('external "diff" not present to test')
 
59
        diff.external_diff('old', old, 'new', new, output, diff_opts=['-u'])
 
60
    except errors.NoDiff:
 
61
        raise tests.TestSkipped('external "diff" not present to test')
94
62
    output.seek(0, 0)
95
63
    lines = output.readlines()
96
64
    output.close()
97
65
    return lines
98
66
 
99
67
 
100
 
class TestDiff(TestCase):
 
68
class TestDiffOptions(tests.TestCase):
 
69
 
 
70
    def test_unified_added(self):
 
71
        """Check for default style '-u' only if no other style specified
 
72
        in 'diff-options'.
 
73
        """
 
74
        # Verify that style defaults to unified, id est '-u' appended
 
75
        # to option list, in the absence of an alternative style.
 
76
        self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
 
77
 
 
78
 
 
79
class TestDiffOptionsScenarios(tests.TestCase):
 
80
 
 
81
    scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
 
82
    style = None # Set by load_tests_apply_scenarios from scenarios
 
83
 
 
84
    def test_unified_not_added(self):
 
85
        # Verify that for all valid style options, '-u' is not
 
86
        # appended to option list.
 
87
        ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
 
88
        self.assertEqual(["%s" % (self.style,)], ret_opts)
 
89
 
 
90
 
 
91
class TestDiff(tests.TestCase):
101
92
 
102
93
    def test_add_nl(self):
103
94
        """diff generates a valid diff for patches that add a newline"""
104
95
        lines = udiff_lines(['boo'], ['boo\n'])
105
96
        self.check_patch(lines)
106
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
97
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
107
98
            ## "expected no-nl, got %r" % lines[4]
108
99
 
109
100
    def test_add_nl_2(self):
112
103
        """
113
104
        lines = udiff_lines(['boo'], ['goo\n'])
114
105
        self.check_patch(lines)
115
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
106
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
116
107
            ## "expected no-nl, got %r" % lines[4]
117
108
 
118
109
    def test_remove_nl(self):
121
112
        """
122
113
        lines = udiff_lines(['boo\n'], ['boo'])
123
114
        self.check_patch(lines)
124
 
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
 
115
        self.assertEqual(lines[5], '\\ No newline at end of file\n')
125
116
            ## "expected no-nl, got %r" % lines[5]
126
117
 
127
118
    def check_patch(self, lines):
128
 
        self.assert_(len(lines) > 1)
 
119
        self.assertTrue(len(lines) > 1)
129
120
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
130
 
        self.assert_(lines[0].startswith ('---'))
 
121
        self.assertTrue(lines[0].startswith ('---'))
131
122
            ## 'No orig line for patch:\n%s' % "".join(lines)
132
 
        self.assert_(lines[1].startswith ('+++'))
 
123
        self.assertTrue(lines[1].startswith ('+++'))
133
124
            ## 'No mod line for patch:\n%s' % "".join(lines)
134
 
        self.assert_(len(lines) > 2)
 
125
        self.assertTrue(len(lines) > 2)
135
126
            ## "No hunks for patch:\n%s" % "".join(lines)
136
 
        self.assert_(lines[2].startswith('@@'))
 
127
        self.assertTrue(lines[2].startswith('@@'))
137
128
            ## "No hunk header for patch:\n%s" % "".join(lines)
138
 
        self.assert_('@@' in lines[2][2:])
 
129
        self.assertTrue('@@' in lines[2][2:])
139
130
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
140
131
 
141
132
    def test_binary_lines(self):
142
 
        self.assertRaises(BinaryFile, udiff_lines, [1023 * 'a' + '\x00'], [])
143
 
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
144
 
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
145
 
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
133
        empty = []
 
134
        uni_lines = [1023 * 'a' + '\x00']
 
135
        self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
 
136
        self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
 
137
        udiff_lines(uni_lines , empty, allow_binary=True)
 
138
        udiff_lines(empty, uni_lines, allow_binary=True)
146
139
 
147
140
    def test_external_diff(self):
148
141
        lines = external_udiff_lines(['boo\n'], ['goo\n'])
158
151
        self.check_patch(lines)
159
152
 
160
153
    def test_external_diff_binary_lang_c(self):
161
 
        old_env = {}
162
154
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
163
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
164
 
        try:
165
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
166
 
            # Older versions of diffutils say "Binary files", newer
167
 
            # versions just say "Files".
168
 
            self.assertContainsRe(lines[0],
169
 
                                  '(Binary f|F)iles old and new differ\n')
170
 
            self.assertEquals(lines[1:], ['\n'])
171
 
        finally:
172
 
            for lang, old_val in old_env.iteritems():
173
 
                osutils.set_or_unset_env(lang, old_val)
 
155
            self.overrideEnv(lang, 'C')
 
156
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
 
157
        # Older versions of diffutils say "Binary files", newer
 
158
        # versions just say "Files".
 
159
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
 
160
        self.assertEqual(lines[1:], ['\n'])
174
161
 
175
162
    def test_no_external_diff(self):
176
163
        """Check that NoDiff is raised when diff is not available"""
177
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
178
 
        orig_path = os.environ['PATH']
179
 
        try:
180
 
            os.environ['PATH'] = ''
181
 
            self.assertRaises(NoDiff, external_diff,
182
 
                              'old', ['boo\n'], 'new', ['goo\n'],
183
 
                              StringIO(), diff_opts=['-u'])
184
 
        finally:
185
 
            os.environ['PATH'] = orig_path
186
 
        
 
164
        # Make sure no 'diff' command is available
 
165
        # XXX: Weird, using None instead of '' breaks the test -- vila 20101216
 
166
        self.overrideEnv('PATH', '')
 
167
        self.assertRaises(errors.NoDiff, diff.external_diff,
 
168
                          'old', ['boo\n'], 'new', ['goo\n'],
 
169
                          StringIO(), diff_opts=['-u'])
 
170
 
187
171
    def test_internal_diff_default(self):
188
172
        # Default internal diff encoding is utf8
189
173
        output = StringIO()
190
 
        internal_diff(u'old_\xb5', ['old_text\n'],
191
 
                    u'new_\xe5', ['new_text\n'], output)
 
174
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
175
                           u'new_\xe5', ['new_text\n'], output)
192
176
        lines = output.getvalue().splitlines(True)
193
177
        self.check_patch(lines)
194
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
178
        self.assertEqual(['--- old_\xc2\xb5\n',
195
179
                           '+++ new_\xc3\xa5\n',
196
180
                           '@@ -1,1 +1,1 @@\n',
197
181
                           '-old_text\n',
202
186
 
203
187
    def test_internal_diff_utf8(self):
204
188
        output = StringIO()
205
 
        internal_diff(u'old_\xb5', ['old_text\n'],
206
 
                    u'new_\xe5', ['new_text\n'], output,
207
 
                    path_encoding='utf8')
 
189
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
190
                           u'new_\xe5', ['new_text\n'], output,
 
191
                           path_encoding='utf8')
208
192
        lines = output.getvalue().splitlines(True)
209
193
        self.check_patch(lines)
210
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
194
        self.assertEqual(['--- old_\xc2\xb5\n',
211
195
                           '+++ new_\xc3\xa5\n',
212
196
                           '@@ -1,1 +1,1 @@\n',
213
197
                           '-old_text\n',
218
202
 
219
203
    def test_internal_diff_iso_8859_1(self):
220
204
        output = StringIO()
221
 
        internal_diff(u'old_\xb5', ['old_text\n'],
222
 
                    u'new_\xe5', ['new_text\n'], output,
223
 
                    path_encoding='iso-8859-1')
 
205
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
206
                           u'new_\xe5', ['new_text\n'], output,
 
207
                           path_encoding='iso-8859-1')
224
208
        lines = output.getvalue().splitlines(True)
225
209
        self.check_patch(lines)
226
 
        self.assertEquals(['--- old_\xb5\n',
 
210
        self.assertEqual(['--- old_\xb5\n',
227
211
                           '+++ new_\xe5\n',
228
212
                           '@@ -1,1 +1,1 @@\n',
229
213
                           '-old_text\n',
234
218
 
235
219
    def test_internal_diff_no_content(self):
236
220
        output = StringIO()
237
 
        internal_diff(u'old', [], u'new', [], output)
 
221
        diff.internal_diff(u'old', [], u'new', [], output)
238
222
        self.assertEqual('', output.getvalue())
239
223
 
240
224
    def test_internal_diff_no_changes(self):
241
225
        output = StringIO()
242
 
        internal_diff(u'old', ['text\n', 'contents\n'],
243
 
                      u'new', ['text\n', 'contents\n'],
244
 
                      output)
 
226
        diff.internal_diff(u'old', ['text\n', 'contents\n'],
 
227
                           u'new', ['text\n', 'contents\n'],
 
228
                           output)
245
229
        self.assertEqual('', output.getvalue())
246
230
 
247
231
    def test_internal_diff_returns_bytes(self):
248
232
        import StringIO
249
233
        output = StringIO.StringIO()
250
 
        internal_diff(u'old_\xb5', ['old_text\n'],
251
 
                    u'new_\xe5', ['new_text\n'], output)
252
 
        self.failUnless(isinstance(output.getvalue(), str),
 
234
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
 
235
                            u'new_\xe5', ['new_text\n'], output)
 
236
        self.assertIsInstance(output.getvalue(), str,
253
237
            'internal_diff should return bytestrings')
254
238
 
255
 
 
256
 
class TestDiffFiles(TestCaseInTempDir):
 
239
    def test_internal_diff_default_context(self):
 
240
        output = StringIO()
 
241
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
 
242
                           'same_text\n','same_text\n','old_text\n'],
 
243
                           'new', ['same_text\n','same_text\n','same_text\n',
 
244
                           'same_text\n','same_text\n','new_text\n'], output)
 
245
        lines = output.getvalue().splitlines(True)
 
246
        self.check_patch(lines)
 
247
        self.assertEqual(['--- old\n',
 
248
                           '+++ new\n',
 
249
                           '@@ -3,4 +3,4 @@\n',
 
250
                           ' same_text\n',
 
251
                           ' same_text\n',
 
252
                           ' same_text\n',
 
253
                           '-old_text\n',
 
254
                           '+new_text\n',
 
255
                           '\n',
 
256
                          ]
 
257
                          , lines)
 
258
 
 
259
    def test_internal_diff_no_context(self):
 
260
        output = StringIO()
 
261
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
 
262
                           'same_text\n','same_text\n','old_text\n'],
 
263
                           'new', ['same_text\n','same_text\n','same_text\n',
 
264
                           'same_text\n','same_text\n','new_text\n'], output,
 
265
                           context_lines=0)
 
266
        lines = output.getvalue().splitlines(True)
 
267
        self.check_patch(lines)
 
268
        self.assertEqual(['--- old\n',
 
269
                           '+++ new\n',
 
270
                           '@@ -6,1 +6,1 @@\n',
 
271
                           '-old_text\n',
 
272
                           '+new_text\n',
 
273
                           '\n',
 
274
                          ]
 
275
                          , lines)
 
276
 
 
277
    def test_internal_diff_more_context(self):
 
278
        output = StringIO()
 
279
        diff.internal_diff('old', ['same_text\n','same_text\n','same_text\n',
 
280
                           'same_text\n','same_text\n','old_text\n'],
 
281
                           'new', ['same_text\n','same_text\n','same_text\n',
 
282
                           'same_text\n','same_text\n','new_text\n'], output,
 
283
                           context_lines=4)
 
284
        lines = output.getvalue().splitlines(True)
 
285
        self.check_patch(lines)
 
286
        self.assertEqual(['--- old\n',
 
287
                           '+++ new\n',
 
288
                           '@@ -2,5 +2,5 @@\n',
 
289
                           ' same_text\n',
 
290
                           ' same_text\n',
 
291
                           ' same_text\n',
 
292
                           ' same_text\n',
 
293
                           '-old_text\n',
 
294
                           '+new_text\n',
 
295
                           '\n',
 
296
                          ]
 
297
                          , lines)
 
298
 
 
299
 
 
300
 
 
301
 
 
302
 
 
303
class TestDiffFiles(tests.TestCaseInTempDir):
257
304
 
258
305
    def test_external_diff_binary(self):
259
306
        """The output when using external diff should use diff's i18n error"""
261
308
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
262
309
 
263
310
        cmd = ['diff', '-u', '--binary', 'old', 'new']
264
 
        open('old', 'wb').write('\x00foobar\n')
265
 
        open('new', 'wb').write('foo\x00bar\n')
 
311
        with open('old', 'wb') as f: f.write('\x00foobar\n')
 
312
        with open('new', 'wb') as f: f.write('foo\x00bar\n')
266
313
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
267
314
                                     stdin=subprocess.PIPE)
268
315
        out, err = pipe.communicate()
272
319
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
273
320
 
274
321
 
275
 
class TestShowDiffTreesHelper(TestCaseWithTransport):
276
 
    """Has a helper for running show_diff_trees"""
277
 
 
278
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
279
 
        output = StringIO()
280
 
        if working_tree is not None:
281
 
            extra_trees = (working_tree,)
282
 
        else:
283
 
            extra_trees = ()
284
 
        show_diff_trees(tree1, tree2, output, specific_files=specific_files,
285
 
                        extra_trees=extra_trees, old_label='old/',
286
 
                        new_label='new/')
287
 
        return output.getvalue()
288
 
 
289
 
 
290
 
class TestDiffDates(TestShowDiffTreesHelper):
 
322
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
323
    output = StringIO()
 
324
    if working_tree is not None:
 
325
        extra_trees = (working_tree,)
 
326
    else:
 
327
        extra_trees = ()
 
328
    diff.show_diff_trees(tree1, tree2, output,
 
329
        specific_files=specific_files,
 
330
        extra_trees=extra_trees, old_label='old/',
 
331
        new_label='new/')
 
332
    return output.getvalue()
 
333
 
 
334
 
 
335
class TestDiffDates(tests.TestCaseWithTransport):
291
336
 
292
337
    def setUp(self):
293
338
        super(TestDiffDates, self).setUp()
328
373
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
329
374
 
330
375
    def test_diff_rev_tree_working_tree(self):
331
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
376
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
332
377
        # note that the date for old/file1 is from rev 2 rather than from
333
378
        # the basis revision (rev 4)
334
379
        self.assertEqualDiff(output, '''\
344
389
    def test_diff_rev_tree_rev_tree(self):
345
390
        tree1 = self.b.repository.revision_tree('rev-2')
346
391
        tree2 = self.b.repository.revision_tree('rev-3')
347
 
        output = self.get_diff(tree1, tree2)
 
392
        output = get_diff_as_string(tree1, tree2)
348
393
        self.assertEqualDiff(output, '''\
349
394
=== modified file 'file2'
350
395
--- old/file2\t2006-04-01 00:00:00 +0000
354
399
+file2 contents at rev 3
355
400
 
356
401
''')
357
 
        
 
402
 
358
403
    def test_diff_add_files(self):
359
 
        tree1 = self.b.repository.revision_tree(None)
 
404
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
360
405
        tree2 = self.b.repository.revision_tree('rev-1')
361
 
        output = self.get_diff(tree1, tree2)
 
406
        output = get_diff_as_string(tree1, tree2)
362
407
        # the files have the epoch time stamp for the tree in which
363
408
        # they don't exist.
364
409
        self.assertEqualDiff(output, '''\
379
424
    def test_diff_remove_files(self):
380
425
        tree1 = self.b.repository.revision_tree('rev-3')
381
426
        tree2 = self.b.repository.revision_tree('rev-4')
382
 
        output = self.get_diff(tree1, tree2)
 
427
        output = get_diff_as_string(tree1, tree2)
383
428
        # the file has the epoch time stamp for the tree in which
384
429
        # it doesn't exist.
385
430
        self.assertEqualDiff(output, '''\
396
441
        self.wt.rename_one('file1', 'file1b')
397
442
        old_tree = self.b.repository.revision_tree('rev-1')
398
443
        new_tree = self.b.repository.revision_tree('rev-4')
399
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'], 
 
444
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
400
445
                            working_tree=self.wt)
401
446
        self.assertContainsRe(out, 'file1\t')
402
447
 
408
453
        self.wt.rename_one('file1', 'dir1/file1')
409
454
        old_tree = self.b.repository.revision_tree('rev-1')
410
455
        new_tree = self.b.repository.revision_tree('rev-4')
411
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'], 
 
456
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
412
457
                            working_tree=self.wt)
413
458
        self.assertContainsRe(out, 'file1\t')
414
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'], 
 
459
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
415
460
                            working_tree=self.wt)
416
461
        self.assertNotContainsRe(out, 'file1\t')
417
462
 
418
463
 
419
 
 
420
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
464
class TestShowDiffTrees(tests.TestCaseWithTransport):
421
465
    """Direct tests for show_diff_trees"""
422
466
 
423
467
    def test_modified_file(self):
428
472
        tree.commit('one', rev_id='rev-1')
429
473
 
430
474
        self.build_tree_contents([('tree/file', 'new contents\n')])
431
 
        diff = self.get_diff(tree.basis_tree(), tree)
432
 
        self.assertContainsRe(diff, "=== modified file 'file'\n")
433
 
        self.assertContainsRe(diff, '--- old/file\t')
434
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
435
 
        self.assertContainsRe(diff, '-contents\n'
436
 
                                    '\\+new contents\n')
 
475
        d = get_diff_as_string(tree.basis_tree(), tree)
 
476
        self.assertContainsRe(d, "=== modified file 'file'\n")
 
477
        self.assertContainsRe(d, '--- old/file\t')
 
478
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
 
479
        self.assertContainsRe(d, '-contents\n'
 
480
                                 '\\+new contents\n')
437
481
 
438
482
    def test_modified_file_in_renamed_dir(self):
439
483
        """Test when a file is modified in a renamed directory."""
445
489
 
446
490
        tree.rename_one('dir', 'other')
447
491
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
448
 
        diff = self.get_diff(tree.basis_tree(), tree)
449
 
        self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
450
 
        self.assertContainsRe(diff, "=== modified file 'other/file'\n")
 
492
        d = get_diff_as_string(tree.basis_tree(), tree)
 
493
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
 
494
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
451
495
        # XXX: This is technically incorrect, because it used to be at another
452
496
        # location. What to do?
453
 
        self.assertContainsRe(diff, '--- old/dir/file\t')
454
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
455
 
        self.assertContainsRe(diff, '-contents\n'
456
 
                                    '\\+new contents\n')
 
497
        self.assertContainsRe(d, '--- old/dir/file\t')
 
498
        self.assertContainsRe(d, '\\+\\+\\+ new/other/file\t')
 
499
        self.assertContainsRe(d, '-contents\n'
 
500
                                 '\\+new contents\n')
457
501
 
458
502
    def test_renamed_directory(self):
459
503
        """Test when only a directory is only renamed."""
464
508
        tree.commit('one', rev_id='rev-1')
465
509
 
466
510
        tree.rename_one('dir', 'newdir')
467
 
        diff = self.get_diff(tree.basis_tree(), tree)
 
511
        d = get_diff_as_string(tree.basis_tree(), tree)
468
512
        # Renaming a directory should be a single "you renamed this dir" even
469
513
        # when there are files inside.
470
 
        self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
 
514
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
471
515
 
472
516
    def test_renamed_file(self):
473
517
        """Test when a file is only renamed."""
477
521
        tree.commit('one', rev_id='rev-1')
478
522
 
479
523
        tree.rename_one('file', 'newname')
480
 
        diff = self.get_diff(tree.basis_tree(), tree)
481
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
 
524
        d = get_diff_as_string(tree.basis_tree(), tree)
 
525
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
482
526
        # We shouldn't have a --- or +++ line, because there is no content
483
527
        # change
484
 
        self.assertNotContainsRe(diff, '---')
 
528
        self.assertNotContainsRe(d, '---')
485
529
 
486
530
    def test_renamed_and_modified_file(self):
487
531
        """Test when a file is only renamed."""
492
536
 
493
537
        tree.rename_one('file', 'newname')
494
538
        self.build_tree_contents([('tree/newname', 'new contents\n')])
495
 
        diff = self.get_diff(tree.basis_tree(), tree)
496
 
        self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
497
 
        self.assertContainsRe(diff, '--- old/file\t')
498
 
        self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
499
 
        self.assertContainsRe(diff, '-contents\n'
500
 
                                    '\\+new contents\n')
 
539
        d = get_diff_as_string(tree.basis_tree(), tree)
 
540
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
 
541
        self.assertContainsRe(d, '--- old/file\t')
 
542
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
 
543
        self.assertContainsRe(d, '-contents\n'
 
544
                                 '\\+new contents\n')
501
545
 
502
546
 
503
547
    def test_internal_diff_exec_property(self):
522
566
        tree.rename_one('c', 'new-c')
523
567
        tree.rename_one('d', 'new-d')
524
568
 
525
 
        diff = self.get_diff(tree.basis_tree(), tree)
526
 
 
527
 
        self.assertContainsRe(diff, r"file 'a'.*\(properties changed:.*\+x to -x.*\)")
528
 
        self.assertContainsRe(diff, r"file 'b'.*\(properties changed:.*-x to \+x.*\)")
529
 
        self.assertContainsRe(diff, r"file 'c'.*\(properties changed:.*\+x to -x.*\)")
530
 
        self.assertContainsRe(diff, r"file 'd'.*\(properties changed:.*-x to \+x.*\)")
531
 
        self.assertNotContainsRe(diff, r"file 'e'")
532
 
        self.assertNotContainsRe(diff, r"file 'f'")
533
 
 
 
569
        d = get_diff_as_string(tree.basis_tree(), tree)
 
570
 
 
571
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
 
572
                                  ".*\+x to -x.*\)")
 
573
        self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
 
574
                                  ".*-x to \+x.*\)")
 
575
        self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
 
576
                                  ".*\+x to -x.*\)")
 
577
        self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
 
578
                                  ".*-x to \+x.*\)")
 
579
        self.assertNotContainsRe(d, r"file 'e'")
 
580
        self.assertNotContainsRe(d, r"file 'f'")
534
581
 
535
582
    def test_binary_unicode_filenames(self):
536
583
        """Test that contents of files are *not* encoded in UTF-8 when there
537
584
        is a binary file in the diff.
538
585
        """
539
586
        # See https://bugs.launchpad.net/bugs/110092.
540
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
587
        self.requireFeature(features.UnicodeFilenameFeature)
541
588
 
542
589
        # This bug isn't triggered with cStringIO.
543
590
        from StringIO import StringIO
551
598
        tree.add([alpha], ['file-id'])
552
599
        tree.add([omega], ['file-id-2'])
553
600
        diff_content = StringIO()
554
 
        show_diff_trees(tree.basis_tree(), tree, diff_content)
555
 
        diff = diff_content.getvalue()
556
 
        self.assertContainsRe(diff, r"=== added file '%s'" % alpha_utf8)
557
 
        self.assertContainsRe(
558
 
            diff, "Binary files a/%s.*and b/%s.* differ\n" % (alpha_utf8, alpha_utf8))
559
 
        self.assertContainsRe(diff, r"=== added file '%s'" % omega_utf8)
560
 
        self.assertContainsRe(diff, r"--- a/%s" % (omega_utf8,))
561
 
        self.assertContainsRe(diff, r"\+\+\+ b/%s" % (omega_utf8,))
 
601
        diff.show_diff_trees(tree.basis_tree(), tree, diff_content)
 
602
        d = diff_content.getvalue()
 
603
        self.assertContainsRe(d, r"=== added file '%s'" % alpha_utf8)
 
604
        self.assertContainsRe(d, "Binary files a/%s.*and b/%s.* differ\n"
 
605
                              % (alpha_utf8, alpha_utf8))
 
606
        self.assertContainsRe(d, r"=== added file '%s'" % omega_utf8)
 
607
        self.assertContainsRe(d, r"--- a/%s" % (omega_utf8,))
 
608
        self.assertContainsRe(d, r"\+\+\+ b/%s" % (omega_utf8,))
562
609
 
563
610
    def test_unicode_filename(self):
564
611
        """Test when the filename are unicode."""
565
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
612
        self.requireFeature(features.UnicodeFilenameFeature)
566
613
 
567
614
        alpha, omega = u'\u03b1', u'\u03c9'
568
615
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
583
630
        tree.add(['add_'+alpha], ['file-id'])
584
631
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
585
632
 
586
 
        diff = self.get_diff(tree.basis_tree(), tree)
587
 
        self.assertContainsRe(diff,
 
633
        d = get_diff_as_string(tree.basis_tree(), tree)
 
634
        self.assertContainsRe(d,
588
635
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
589
 
        self.assertContainsRe(diff, "=== added file 'add_%s'"%autf8)
590
 
        self.assertContainsRe(diff, "=== modified file 'mod_%s'"%autf8)
591
 
        self.assertContainsRe(diff, "=== removed file 'del_%s'"%autf8)
592
 
 
593
 
 
594
 
class DiffWasIs(DiffPath):
 
636
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
 
637
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
 
638
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
 
639
 
 
640
    def test_unicode_filename_path_encoding(self):
 
641
        """Test for bug #382699: unicode filenames on Windows should be shown
 
642
        in user encoding.
 
643
        """
 
644
        self.requireFeature(features.UnicodeFilenameFeature)
 
645
        # The word 'test' in Russian
 
646
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
647
        directory = _russian_test + u'/'
 
648
        test_txt = _russian_test + u'.txt'
 
649
        u1234 = u'\u1234.txt'
 
650
 
 
651
        tree = self.make_branch_and_tree('.')
 
652
        self.build_tree_contents([
 
653
            (test_txt, 'foo\n'),
 
654
            (u1234, 'foo\n'),
 
655
            (directory, None),
 
656
            ])
 
657
        tree.add([test_txt, u1234, directory])
 
658
 
 
659
        sio = StringIO()
 
660
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
661
            path_encoding='cp1251')
 
662
 
 
663
        output = subst_dates(sio.getvalue())
 
664
        shouldbe = ('''\
 
665
=== added directory '%(directory)s'
 
666
=== added file '%(test_txt)s'
 
667
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
668
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
669
@@ -0,0 +1,1 @@
 
670
+foo
 
671
 
 
672
=== added file '?.txt'
 
673
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
674
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
675
@@ -0,0 +1,1 @@
 
676
+foo
 
677
 
 
678
''' % {'directory': _russian_test.encode('cp1251'),
 
679
       'test_txt': test_txt.encode('cp1251'),
 
680
      })
 
681
        self.assertEqualDiff(output, shouldbe)
 
682
 
 
683
 
 
684
class DiffWasIs(diff.DiffPath):
595
685
 
596
686
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
597
687
        self.to_file.write('was: ')
601
691
        pass
602
692
 
603
693
 
604
 
class TestDiffTree(TestCaseWithTransport):
 
694
class TestDiffTree(tests.TestCaseWithTransport):
605
695
 
606
696
    def setUp(self):
607
 
        TestCaseWithTransport.setUp(self)
 
697
        super(TestDiffTree, self).setUp()
608
698
        self.old_tree = self.make_branch_and_tree('old-tree')
609
699
        self.old_tree.lock_write()
610
700
        self.addCleanup(self.old_tree.unlock)
611
701
        self.new_tree = self.make_branch_and_tree('new-tree')
612
702
        self.new_tree.lock_write()
613
703
        self.addCleanup(self.new_tree.unlock)
614
 
        self.differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
704
        self.differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
615
705
 
616
706
    def test_diff_text(self):
617
707
        self.build_tree_contents([('old-tree/olddir/',),
622
712
                                  ('new-tree/newdir/newfile', 'new\n')])
623
713
        self.new_tree.add('newdir')
624
714
        self.new_tree.add('newdir/newfile', 'file-id')
625
 
        differ = DiffText(self.old_tree, self.new_tree, StringIO())
 
715
        differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
626
716
        differ.diff_text('file-id', None, 'old label', 'new label')
627
717
        self.assertEqual(
628
718
            '--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
657
747
        self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
658
748
 
659
749
    def test_diff_symlink(self):
660
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
750
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
661
751
        differ.diff_symlink('old target', None)
662
752
        self.assertEqual("=== target was 'old target'\n",
663
753
                         differ.to_file.getvalue())
664
754
 
665
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
755
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
666
756
        differ.diff_symlink(None, 'new target')
667
757
        self.assertEqual("=== target is 'new target'\n",
668
758
                         differ.to_file.getvalue())
669
759
 
670
 
        differ = DiffSymlink(self.old_tree, self.new_tree, StringIO())
 
760
        differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
671
761
        differ.diff_symlink('old target', 'new target')
672
762
        self.assertEqual("=== target changed 'old target' => 'new target'\n",
673
763
                         differ.to_file.getvalue())
688
778
             ' \@\@\n-old\n\+new\n\n')
689
779
 
690
780
    def test_diff_kind_change(self):
691
 
        self.requireFeature(tests.SymlinkFeature)
 
781
        self.requireFeature(features.SymlinkFeature)
692
782
        self.build_tree_contents([('old-tree/olddir/',),
693
783
                                  ('old-tree/olddir/oldfile', 'old\n')])
694
784
        self.old_tree.add('olddir')
703
793
            r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+0,0'
704
794
             ' \@\@\n-old\n\n')
705
795
        self.assertContainsRe(self.differ.to_file.getvalue(),
706
 
                              "=== target is 'new'\n")
 
796
                              "=== target is u'new'\n")
707
797
 
708
798
    def test_diff_directory(self):
709
799
        self.build_tree(['new-tree/new-dir/'])
723
813
 
724
814
    def test_register_diff(self):
725
815
        self.create_old_new()
726
 
        old_diff_factories = DiffTree.diff_factories
727
 
        DiffTree.diff_factories=old_diff_factories[:]
728
 
        DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
 
816
        old_diff_factories = diff.DiffTree.diff_factories
 
817
        diff.DiffTree.diff_factories=old_diff_factories[:]
 
818
        diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
729
819
        try:
730
 
            differ = DiffTree(self.old_tree, self.new_tree, StringIO())
 
820
            differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
731
821
        finally:
732
 
            DiffTree.diff_factories = old_diff_factories
 
822
            diff.DiffTree.diff_factories = old_diff_factories
733
823
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
734
824
        self.assertNotContainsRe(
735
825
            differ.to_file.getvalue(),
740
830
 
741
831
    def test_extra_factories(self):
742
832
        self.create_old_new()
743
 
        differ = DiffTree(self.old_tree, self.new_tree, StringIO(),
744
 
                            extra_factories=[DiffWasIs.from_diff_tree])
 
833
        differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
 
834
                               extra_factories=[DiffWasIs.from_diff_tree])
745
835
        differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
746
836
        self.assertNotContainsRe(
747
837
            differ.to_file.getvalue(),
760
850
            '.*a-file(.|\n)*b-file')
761
851
 
762
852
 
763
 
class TestPatienceDiffLib(TestCase):
 
853
class TestPatienceDiffLib(tests.TestCase):
764
854
 
765
855
    def setUp(self):
766
856
        super(TestPatienceDiffLib, self).setUp()
767
 
        self._unique_lcs = bzrlib._patiencediff_py.unique_lcs_py
768
 
        self._recurse_matches = bzrlib._patiencediff_py.recurse_matches_py
 
857
        self._unique_lcs = _patiencediff_py.unique_lcs_py
 
858
        self._recurse_matches = _patiencediff_py.recurse_matches_py
769
859
        self._PatienceSequenceMatcher = \
770
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
860
            _patiencediff_py.PatienceSequenceMatcher_py
 
861
 
 
862
    def test_diff_unicode_string(self):
 
863
        a = ''.join([unichr(i) for i in range(4000, 4500, 3)])
 
864
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
 
865
        sm = self._PatienceSequenceMatcher(None, a, b)
 
866
        mb = sm.get_matching_blocks()
 
867
        self.assertEqual(35, len(mb))
771
868
 
772
869
    def test_unique_lcs(self):
773
870
        unique_lcs = self._unique_lcs
774
 
        self.assertEquals(unique_lcs('', ''), [])
775
 
        self.assertEquals(unique_lcs('', 'a'), [])
776
 
        self.assertEquals(unique_lcs('a', ''), [])
777
 
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
778
 
        self.assertEquals(unique_lcs('a', 'b'), [])
779
 
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
780
 
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
781
 
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
782
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
 
871
        self.assertEqual(unique_lcs('', ''), [])
 
872
        self.assertEqual(unique_lcs('', 'a'), [])
 
873
        self.assertEqual(unique_lcs('a', ''), [])
 
874
        self.assertEqual(unique_lcs('a', 'a'), [(0,0)])
 
875
        self.assertEqual(unique_lcs('a', 'b'), [])
 
876
        self.assertEqual(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
 
877
        self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
 
878
        self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
 
879
        self.assertEqual(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
783
880
                                                         (3,3), (4,4)])
784
 
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
 
881
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2,1)])
785
882
 
786
883
    def test_recurse_matches(self):
787
884
        def test_one(a, b, matches):
788
885
            test_matches = []
789
886
            self._recurse_matches(
790
887
                a, b, 0, 0, len(a), len(b), test_matches, 10)
791
 
            self.assertEquals(test_matches, matches)
 
888
            self.assertEqual(test_matches, matches)
792
889
 
793
890
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
794
891
                 [(0, 0), (2, 2), (4, 4)])
800
897
        test_one('abcdbce', 'afbcgdbce', [(0,0), (1, 2), (2, 3), (3, 5),
801
898
                                          (4, 6), (5, 7), (6, 8)])
802
899
 
803
 
        # recurse_matches doesn't match non-unique 
 
900
        # recurse_matches doesn't match non-unique
804
901
        # lines surrounded by bogus text.
805
902
        # The update has been done in patiencediff.SequenceMatcher instead
806
903
 
899
996
    def test_opcodes(self):
900
997
        def chk_ops(a, b, expected_codes):
901
998
            s = self._PatienceSequenceMatcher(None, a, b)
902
 
            self.assertEquals(expected_codes, s.get_opcodes())
 
999
            self.assertEqual(expected_codes, s.get_opcodes())
903
1000
 
904
1001
        chk_ops('', '', [])
905
1002
        chk_ops([], [], [])
943
1040
                 ('delete', 1,2, 1,1),
944
1041
                 ('equal',  2,3, 1,2),
945
1042
                ])
946
 
        chk_ops('aBccDe', 'abccde', 
 
1043
        chk_ops('aBccDe', 'abccde',
947
1044
                [('equal',   0,1, 0,1),
948
1045
                 ('replace', 1,5, 1,5),
949
1046
                 ('equal',   5,6, 5,6),
950
1047
                ])
951
 
        chk_ops('aBcDec', 'abcdec', 
 
1048
        chk_ops('aBcDec', 'abcdec',
952
1049
                [('equal',   0,1, 0,1),
953
1050
                 ('replace', 1,2, 1,2),
954
1051
                 ('equal',   2,3, 2,3),
955
1052
                 ('replace', 3,4, 3,4),
956
1053
                 ('equal',   4,6, 4,6),
957
1054
                ])
958
 
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
 
1055
        chk_ops('aBcdEcdFg', 'abcdecdfg',
959
1056
                [('equal',   0,1, 0,1),
960
1057
                 ('replace', 1,8, 1,8),
961
1058
                 ('equal',   8,9, 8,9)
962
1059
                ])
963
 
        chk_ops('aBcdEeXcdFg', 'abcdecdfg', 
 
1060
        chk_ops('aBcdEeXcdFg', 'abcdecdfg',
964
1061
                [('equal',   0,1, 0,1),
965
1062
                 ('replace', 1,2, 1,2),
966
1063
                 ('equal',   2,4, 2,4),
975
1072
    def test_grouped_opcodes(self):
976
1073
        def chk_ops(a, b, expected_codes, n=3):
977
1074
            s = self._PatienceSequenceMatcher(None, a, b)
978
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
 
1075
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
979
1076
 
980
1077
        chk_ops('', '', [])
981
1078
        chk_ops([], [], [])
1026
1123
    """
1027
1124
    gnxrf_netf = ['svyr*']
1028
1125
    gnxrf_bcgvbaf = ['ab-erphefr']
1029
 
  
 
1126
 
1030
1127
    qrs eha(frys, svyr_yvfg, ab_erphefr=Snyfr):
1031
1128
        sebz omeyvo.nqq vzcbeg fzneg_nqq, nqq_ercbegre_cevag, nqq_ercbegre_ahyy
1032
1129
        vs vf_dhvrg():
1040
1137
'''.splitlines(True), '''\
1041
1138
    trg nqqrq jura lbh nqq n svyr va gur qverpgbel.
1042
1139
 
1043
 
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl 
 
1140
    --qel-eha jvyy fubj juvpu svyrf jbhyq or nqqrq, ohg abg npghnyyl
1044
1141
    nqq gurz.
1045
1142
    """
1046
1143
    gnxrf_netf = ['svyr*']
1073
1170
                 'how are you today?\n']
1074
1171
        txt_b = ['hello there\n',
1075
1172
                 'how are you today?\n']
1076
 
        unified_diff = bzrlib.patiencediff.unified_diff
 
1173
        unified_diff = patiencediff.unified_diff
1077
1174
        psm = self._PatienceSequenceMatcher
1078
 
        self.assertEquals([ '---  \n',
1079
 
                           '+++  \n',
 
1175
        self.assertEqual(['--- \n',
 
1176
                           '+++ \n',
1080
1177
                           '@@ -1,3 +1,2 @@\n',
1081
1178
                           ' hello there\n',
1082
1179
                           '-world\n',
1087
1184
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1088
1185
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1089
1186
        # This is the result with LongestCommonSubstring matching
1090
 
        self.assertEquals(['---  \n',
1091
 
                           '+++  \n',
 
1187
        self.assertEqual(['--- \n',
 
1188
                           '+++ \n',
1092
1189
                           '@@ -1,6 +1,11 @@\n',
1093
1190
                           ' a\n',
1094
1191
                           ' b\n',
1103
1200
                           ' f\n']
1104
1201
                          , list(unified_diff(txt_a, txt_b)))
1105
1202
        # And the patience diff
1106
 
        self.assertEquals(['---  \n',
1107
 
                           '+++  \n',
 
1203
        self.assertEqual(['--- \n',
 
1204
                           '+++ \n',
1108
1205
                           '@@ -4,6 +4,11 @@\n',
1109
1206
                           ' d\n',
1110
1207
                           ' e\n',
1121
1218
                          , list(unified_diff(txt_a, txt_b,
1122
1219
                                 sequencematcher=psm)))
1123
1220
 
 
1221
    def test_patience_unified_diff_with_dates(self):
 
1222
        txt_a = ['hello there\n',
 
1223
                 'world\n',
 
1224
                 'how are you today?\n']
 
1225
        txt_b = ['hello there\n',
 
1226
                 'how are you today?\n']
 
1227
        unified_diff = patiencediff.unified_diff
 
1228
        psm = self._PatienceSequenceMatcher
 
1229
        self.assertEqual(['--- a\t2008-08-08\n',
 
1230
                           '+++ b\t2008-09-09\n',
 
1231
                           '@@ -1,3 +1,2 @@\n',
 
1232
                           ' hello there\n',
 
1233
                           '-world\n',
 
1234
                           ' how are you today?\n'
 
1235
                          ]
 
1236
                          , list(unified_diff(txt_a, txt_b,
 
1237
                                 fromfile='a', tofile='b',
 
1238
                                 fromfiledate='2008-08-08',
 
1239
                                 tofiledate='2008-09-09',
 
1240
                                 sequencematcher=psm)))
 
1241
 
1124
1242
 
1125
1243
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1126
1244
 
1127
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1245
    _test_needs_features = [features.compiled_patiencediff_feature]
1128
1246
 
1129
1247
    def setUp(self):
1130
1248
        super(TestPatienceDiffLib_c, self).setUp()
1131
 
        import bzrlib._patiencediff_c
1132
 
        self._unique_lcs = bzrlib._patiencediff_c.unique_lcs_c
1133
 
        self._recurse_matches = bzrlib._patiencediff_c.recurse_matches_c
 
1249
        from bzrlib import _patiencediff_c
 
1250
        self._unique_lcs = _patiencediff_c.unique_lcs_c
 
1251
        self._recurse_matches = _patiencediff_c.recurse_matches_c
1134
1252
        self._PatienceSequenceMatcher = \
1135
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
 
1253
            _patiencediff_c.PatienceSequenceMatcher_c
1136
1254
 
1137
1255
    def test_unhashable(self):
1138
1256
        """We should get a proper exception here."""
1148
1266
                                         None, ['valid'], ['valid', []])
1149
1267
 
1150
1268
 
1151
 
class TestPatienceDiffLibFiles(TestCaseInTempDir):
 
1269
class TestPatienceDiffLibFiles(tests.TestCaseInTempDir):
1152
1270
 
1153
1271
    def setUp(self):
1154
1272
        super(TestPatienceDiffLibFiles, self).setUp()
1155
1273
        self._PatienceSequenceMatcher = \
1156
 
            bzrlib._patiencediff_py.PatienceSequenceMatcher_py
 
1274
            _patiencediff_py.PatienceSequenceMatcher_py
1157
1275
 
1158
1276
    def test_patience_unified_diff_files(self):
1159
1277
        txt_a = ['hello there\n',
1161
1279
                 'how are you today?\n']
1162
1280
        txt_b = ['hello there\n',
1163
1281
                 'how are you today?\n']
1164
 
        open('a1', 'wb').writelines(txt_a)
1165
 
        open('b1', 'wb').writelines(txt_b)
 
1282
        with open('a1', 'wb') as f: f.writelines(txt_a)
 
1283
        with open('b1', 'wb') as f: f.writelines(txt_b)
1166
1284
 
1167
 
        unified_diff_files = bzrlib.patiencediff.unified_diff_files
 
1285
        unified_diff_files = patiencediff.unified_diff_files
1168
1286
        psm = self._PatienceSequenceMatcher
1169
 
        self.assertEquals(['--- a1 \n',
1170
 
                           '+++ b1 \n',
 
1287
        self.assertEqual(['--- a1\n',
 
1288
                           '+++ b1\n',
1171
1289
                           '@@ -1,3 +1,2 @@\n',
1172
1290
                           ' hello there\n',
1173
1291
                           '-world\n',
1178
1296
 
1179
1297
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1180
1298
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1181
 
        open('a2', 'wb').writelines(txt_a)
1182
 
        open('b2', 'wb').writelines(txt_b)
 
1299
        with open('a2', 'wb') as f: f.writelines(txt_a)
 
1300
        with open('b2', 'wb') as f: f.writelines(txt_b)
1183
1301
 
1184
1302
        # This is the result with LongestCommonSubstring matching
1185
 
        self.assertEquals(['--- a2 \n',
1186
 
                           '+++ b2 \n',
 
1303
        self.assertEqual(['--- a2\n',
 
1304
                           '+++ b2\n',
1187
1305
                           '@@ -1,6 +1,11 @@\n',
1188
1306
                           ' a\n',
1189
1307
                           ' b\n',
1199
1317
                          , list(unified_diff_files('a2', 'b2')))
1200
1318
 
1201
1319
        # And the patience diff
1202
 
        self.assertEquals(['--- a2 \n',
1203
 
                           '+++ b2 \n',
1204
 
                           '@@ -4,6 +4,11 @@\n',
1205
 
                           ' d\n',
1206
 
                           ' e\n',
1207
 
                           ' f\n',
1208
 
                           '+x\n',
1209
 
                           '+y\n',
1210
 
                           '+d\n',
1211
 
                           '+e\n',
1212
 
                           '+f\n',
1213
 
                           ' g\n',
1214
 
                           ' h\n',
1215
 
                           ' i\n',
1216
 
                          ]
1217
 
                          , list(unified_diff_files('a2', 'b2',
1218
 
                                 sequencematcher=psm)))
 
1320
        self.assertEqual(['--- a2\n',
 
1321
                          '+++ b2\n',
 
1322
                          '@@ -4,6 +4,11 @@\n',
 
1323
                          ' d\n',
 
1324
                          ' e\n',
 
1325
                          ' f\n',
 
1326
                          '+x\n',
 
1327
                          '+y\n',
 
1328
                          '+d\n',
 
1329
                          '+e\n',
 
1330
                          '+f\n',
 
1331
                          ' g\n',
 
1332
                          ' h\n',
 
1333
                          ' i\n'],
 
1334
                         list(unified_diff_files('a2', 'b2',
 
1335
                                                 sequencematcher=psm)))
1219
1336
 
1220
1337
 
1221
1338
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1222
1339
 
1223
 
    _test_needs_features = [CompiledPatienceDiffFeature]
 
1340
    _test_needs_features = [features.compiled_patiencediff_feature]
1224
1341
 
1225
1342
    def setUp(self):
1226
1343
        super(TestPatienceDiffLibFiles_c, self).setUp()
1227
 
        import bzrlib._patiencediff_c
 
1344
        from bzrlib import _patiencediff_c
1228
1345
        self._PatienceSequenceMatcher = \
1229
 
            bzrlib._patiencediff_c.PatienceSequenceMatcher_c
1230
 
 
1231
 
 
1232
 
class TestUsingCompiledIfAvailable(TestCase):
 
1346
            _patiencediff_c.PatienceSequenceMatcher_c
 
1347
 
 
1348
 
 
1349
class TestUsingCompiledIfAvailable(tests.TestCase):
1233
1350
 
1234
1351
    def test_PatienceSequenceMatcher(self):
1235
 
        if CompiledPatienceDiffFeature.available():
 
1352
        if features.compiled_patiencediff_feature.available():
1236
1353
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1237
1354
            self.assertIs(PatienceSequenceMatcher_c,
1238
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1355
                          patiencediff.PatienceSequenceMatcher)
1239
1356
        else:
1240
1357
            from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1241
1358
            self.assertIs(PatienceSequenceMatcher_py,
1242
 
                          bzrlib.patiencediff.PatienceSequenceMatcher)
 
1359
                          patiencediff.PatienceSequenceMatcher)
1243
1360
 
1244
1361
    def test_unique_lcs(self):
1245
 
        if CompiledPatienceDiffFeature.available():
 
1362
        if features.compiled_patiencediff_feature.available():
1246
1363
            from bzrlib._patiencediff_c import unique_lcs_c
1247
1364
            self.assertIs(unique_lcs_c,
1248
 
                          bzrlib.patiencediff.unique_lcs)
 
1365
                          patiencediff.unique_lcs)
1249
1366
        else:
1250
1367
            from bzrlib._patiencediff_py import unique_lcs_py
1251
1368
            self.assertIs(unique_lcs_py,
1252
 
                          bzrlib.patiencediff.unique_lcs)
 
1369
                          patiencediff.unique_lcs)
1253
1370
 
1254
1371
    def test_recurse_matches(self):
1255
 
        if CompiledPatienceDiffFeature.available():
 
1372
        if features.compiled_patiencediff_feature.available():
1256
1373
            from bzrlib._patiencediff_c import recurse_matches_c
1257
1374
            self.assertIs(recurse_matches_c,
1258
 
                          bzrlib.patiencediff.recurse_matches)
 
1375
                          patiencediff.recurse_matches)
1259
1376
        else:
1260
1377
            from bzrlib._patiencediff_py import recurse_matches_py
1261
1378
            self.assertIs(recurse_matches_py,
1262
 
                          bzrlib.patiencediff.recurse_matches)
1263
 
 
1264
 
 
1265
 
class TestDiffFromTool(TestCaseWithTransport):
 
1379
                          patiencediff.recurse_matches)
 
1380
 
 
1381
 
 
1382
class TestDiffFromTool(tests.TestCaseWithTransport):
1266
1383
 
1267
1384
    def test_from_string(self):
1268
 
        diff_obj = DiffFromTool.from_string('diff', None, None, None)
 
1385
        diff_obj = diff.DiffFromTool.from_string('diff', None, None, None)
1269
1386
        self.addCleanup(diff_obj.finish)
1270
 
        self.assertEqual(['diff', '%(old_path)s', '%(new_path)s'],
 
1387
        self.assertEqual(['diff', '@old_path', '@new_path'],
1271
1388
            diff_obj.command_template)
1272
1389
 
1273
1390
    def test_from_string_u5(self):
1274
 
        diff_obj = DiffFromTool.from_string('diff -u\\ 5', None, None, None)
 
1391
        diff_obj = diff.DiffFromTool.from_string('diff "-u 5"',
 
1392
                                                 None, None, None)
1275
1393
        self.addCleanup(diff_obj.finish)
1276
 
        self.assertEqual(['diff', '-u 5', '%(old_path)s', '%(new_path)s'],
 
1394
        self.assertEqual(['diff', '-u 5', '@old_path', '@new_path'],
1277
1395
                         diff_obj.command_template)
1278
1396
        self.assertEqual(['diff', '-u 5', 'old-path', 'new-path'],
1279
1397
                         diff_obj._get_command('old-path', 'new-path'))
1280
1398
 
 
1399
    def test_from_string_path_with_backslashes(self):
 
1400
        self.requireFeature(features.backslashdir_feature)
 
1401
        tool = 'C:\\Tools\\Diff.exe'
 
1402
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
 
1403
        self.addCleanup(diff_obj.finish)
 
1404
        self.assertEqual(['C:\\Tools\\Diff.exe', '@old_path', '@new_path'],
 
1405
                         diff_obj.command_template)
 
1406
        self.assertEqual(['C:\\Tools\\Diff.exe', 'old-path', 'new-path'],
 
1407
                         diff_obj._get_command('old-path', 'new-path'))
 
1408
 
1281
1409
    def test_execute(self):
1282
1410
        output = StringIO()
1283
 
        diff_obj = DiffFromTool(['python', '-c',
1284
 
                                 'print "%(old_path)s %(new_path)s"'],
1285
 
                                None, None, output)
 
1411
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1412
                                      'print "@old_path @new_path"'],
 
1413
                                     None, None, output)
1286
1414
        self.addCleanup(diff_obj.finish)
1287
1415
        diff_obj._execute('old', 'new')
1288
1416
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1289
1417
 
1290
 
    def test_excute_missing(self):
1291
 
        diff_obj = DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1292
 
                                None, None, None)
 
1418
    def test_execute_missing(self):
 
1419
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
 
1420
                                     None, None, None)
1293
1421
        self.addCleanup(diff_obj.finish)
1294
 
        e = self.assertRaises(ExecutableMissing, diff_obj._execute, 'old',
1295
 
                              'new')
 
1422
        e = self.assertRaises(errors.ExecutableMissing, diff_obj._execute,
 
1423
                              'old', 'new')
1296
1424
        self.assertEqual('a-tool-which-is-unlikely-to-exist could not be found'
1297
1425
                         ' on this machine', str(e))
1298
1426
 
1299
1427
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1300
 
        self.requireFeature(AttribFeature)
 
1428
        self.requireFeature(features.AttribFeature)
1301
1429
        output = StringIO()
1302
1430
        tree = self.make_branch_and_tree('tree')
1303
1431
        self.build_tree_contents([('tree/file', 'content')])
1305
1433
        tree.commit('old tree')
1306
1434
        tree.lock_read()
1307
1435
        self.addCleanup(tree.unlock)
1308
 
        diff_obj = DiffFromTool(['python', '-c',
1309
 
                                 'print "%(old_path)s %(new_path)s"'],
1310
 
                                tree, tree, output)
 
1436
        basis_tree = tree.basis_tree()
 
1437
        basis_tree.lock_read()
 
1438
        self.addCleanup(basis_tree.unlock)
 
1439
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1440
                                      'print "@old_path @new_path"'],
 
1441
                                     basis_tree, tree, output)
1311
1442
        diff_obj._prepare_files('file-id', 'file', 'file')
1312
 
        self.assertReadableByAttrib(diff_obj._root, 'old\\file', r'old\\file')
1313
 
        self.assertReadableByAttrib(diff_obj._root, 'new\\file', r'new\\file')
 
1443
        # The old content should be readonly
 
1444
        self.assertReadableByAttrib(diff_obj._root, 'old\\file',
 
1445
                                    r'R.*old\\file$')
 
1446
        # The new content should use the tree object, not a 'new' file anymore
 
1447
        self.assertEndsWith(tree.basedir, 'work/tree')
 
1448
        self.assertReadableByAttrib(tree.basedir, 'file', r'work\\tree\\file$')
1314
1449
 
1315
1450
    def assertReadableByAttrib(self, cwd, relpath, regex):
1316
1451
        proc = subprocess.Popen(['attrib', relpath],
1317
1452
                                stdout=subprocess.PIPE,
1318
1453
                                cwd=cwd)
1319
 
        proc.wait()
1320
 
        result = proc.stdout.read()
1321
 
        self.assertContainsRe(result, regex)
 
1454
        (result, err) = proc.communicate()
 
1455
        self.assertContainsRe(result.replace('\r\n', '\n'), regex)
1322
1456
 
1323
1457
    def test_prepare_files(self):
1324
1458
        output = StringIO()
1327
1461
        self.build_tree_contents([('tree/oldname2', 'oldcontent2')])
1328
1462
        tree.add('oldname', 'file-id')
1329
1463
        tree.add('oldname2', 'file2-id')
1330
 
        tree.commit('old tree', timestamp=0)
 
1464
        # Earliest allowable date on FAT32 filesystems is 1980-01-01
 
1465
        tree.commit('old tree', timestamp=315532800)
1331
1466
        tree.rename_one('oldname', 'newname')
1332
1467
        tree.rename_one('oldname2', 'newname2')
1333
1468
        self.build_tree_contents([('tree/newname', 'newcontent')])
1337
1472
        self.addCleanup(old_tree.unlock)
1338
1473
        tree.lock_read()
1339
1474
        self.addCleanup(tree.unlock)
1340
 
        diff_obj = DiffFromTool(['python', '-c',
1341
 
                                 'print "%(old_path)s %(new_path)s"'],
1342
 
                                old_tree, tree, output)
 
1475
        diff_obj = diff.DiffFromTool(['python', '-c',
 
1476
                                      'print "@old_path @new_path"'],
 
1477
                                     old_tree, tree, output)
1343
1478
        self.addCleanup(diff_obj.finish)
1344
1479
        self.assertContainsRe(diff_obj._root, 'bzr-diff-[^/]*')
1345
1480
        old_path, new_path = diff_obj._prepare_files('file-id', 'oldname',
1346
1481
                                                     'newname')
1347
1482
        self.assertContainsRe(old_path, 'old/oldname$')
1348
 
        self.assertEqual(0, os.stat(old_path).st_mtime)
1349
 
        self.assertContainsRe(new_path, 'new/newname$')
 
1483
        self.assertEqual(315532800, os.stat(old_path).st_mtime)
 
1484
        self.assertContainsRe(new_path, 'tree/newname$')
1350
1485
        self.assertFileEqual('oldcontent', old_path)
1351
1486
        self.assertFileEqual('newcontent', new_path)
1352
1487
        if osutils.host_os_dereferences_symlinks():
1353
1488
            self.assertTrue(os.path.samefile('tree/newname', new_path))
1354
1489
        # make sure we can create files with the same parent directories
1355
1490
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
 
1491
 
 
1492
 
 
1493
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1494
 
 
1495
    def test_encodable_filename(self):
 
1496
        # Just checks file path for external diff tool.
 
1497
        # We cannot change CPython's internal encoding used by os.exec*.
 
1498
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1499
                                    None, None, None)
 
1500
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1501
            encoding = scenario['encoding']
 
1502
            dirname = scenario['info']['directory']
 
1503
            filename = scenario['info']['filename']
 
1504
 
 
1505
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1506
            relpath = dirname + u'/' + filename
 
1507
            fullpath = diffobj._safe_filename('safe', relpath)
 
1508
            self.assertEqual(fullpath,
 
1509
                             fullpath.encode(encoding).decode(encoding))
 
1510
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
 
1511
 
 
1512
    def test_unencodable_filename(self):
 
1513
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1514
                                    None, None, None)
 
1515
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1516
            encoding = scenario['encoding']
 
1517
            dirname = scenario['info']['directory']
 
1518
            filename = scenario['info']['filename']
 
1519
 
 
1520
            if encoding == 'iso-8859-1':
 
1521
                encoding = 'iso-8859-2'
 
1522
            else:
 
1523
                encoding = 'iso-8859-1'
 
1524
 
 
1525
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1526
            relpath = dirname + u'/' + filename
 
1527
            fullpath = diffobj._safe_filename('safe', relpath)
 
1528
            self.assertEqual(fullpath,
 
1529
                             fullpath.encode(encoding).decode(encoding))
 
1530
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
 
1531
 
 
1532
 
 
1533
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
 
1534
 
 
1535
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
 
1536
        """Call get_trees_and_branches_to_diff_locked."""
 
1537
        return diff.get_trees_and_branches_to_diff_locked(
 
1538
            path_list, revision_specs, old_url, new_url, self.addCleanup)
 
1539
 
 
1540
    def test_basic(self):
 
1541
        tree = self.make_branch_and_tree('tree')
 
1542
        (old_tree, new_tree,
 
1543
         old_branch, new_branch,
 
1544
         specific_files, extra_trees) = self.call_gtabtd(
 
1545
             ['tree'], None, None, None)
 
1546
 
 
1547
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1548
        self.assertEqual(_mod_revision.NULL_REVISION,
 
1549
                         old_tree.get_revision_id())
 
1550
        self.assertEqual(tree.basedir, new_tree.basedir)
 
1551
        self.assertEqual(tree.branch.base, old_branch.base)
 
1552
        self.assertEqual(tree.branch.base, new_branch.base)
 
1553
        self.assertIs(None, specific_files)
 
1554
        self.assertIs(None, extra_trees)
 
1555
 
 
1556
    def test_with_rev_specs(self):
 
1557
        tree = self.make_branch_and_tree('tree')
 
1558
        self.build_tree_contents([('tree/file', 'oldcontent')])
 
1559
        tree.add('file', 'file-id')
 
1560
        tree.commit('old tree', timestamp=0, rev_id="old-id")
 
1561
        self.build_tree_contents([('tree/file', 'newcontent')])
 
1562
        tree.commit('new tree', timestamp=0, rev_id="new-id")
 
1563
 
 
1564
        revisions = [revisionspec.RevisionSpec.from_string('1'),
 
1565
                     revisionspec.RevisionSpec.from_string('2')]
 
1566
        (old_tree, new_tree,
 
1567
         old_branch, new_branch,
 
1568
         specific_files, extra_trees) = self.call_gtabtd(
 
1569
            ['tree'], revisions, None, None)
 
1570
 
 
1571
        self.assertIsInstance(old_tree, revisiontree.RevisionTree)
 
1572
        self.assertEqual("old-id", old_tree.get_revision_id())
 
1573
        self.assertIsInstance(new_tree, revisiontree.RevisionTree)
 
1574
        self.assertEqual("new-id", new_tree.get_revision_id())
 
1575
        self.assertEqual(tree.branch.base, old_branch.base)
 
1576
        self.assertEqual(tree.branch.base, new_branch.base)
 
1577
        self.assertIs(None, specific_files)
 
1578
        self.assertEqual(tree.basedir, extra_trees[0].basedir)