~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-05-06 11:40:10 UTC
  • mfrom: (3400.1.3 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20080506114010-jwclr2qtiekvawjg
Remove erroneous creation of branch-name file in cmd_branch

Show diffs side-by-side

added added

removed removed

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