/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

Bundle bzr-fastimport.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2014, 2016, 2017 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
import os
18
18
from cStringIO import StringIO
19
19
import subprocess
20
 
import sys
21
20
import tempfile
22
21
 
23
22
from bzrlib import (
32
31
    tests,
33
32
    transform,
34
33
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
 
from bzrlib.tests import test_win32utils
37
 
 
38
 
 
39
 
class _AttribFeature(tests.Feature):
40
 
 
41
 
    def _probe(self):
42
 
        if (sys.platform not in ('cygwin', 'win32')):
43
 
            return False
44
 
        try:
45
 
            proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
46
 
        except OSError, e:
47
 
            return False
48
 
        return (0 == proc.wait())
49
 
 
50
 
    def feature_name(self):
51
 
        return 'attrib Windows command-line tool'
52
 
 
53
 
AttribFeature = _AttribFeature()
54
 
 
55
 
 
56
 
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
57
 
                                    'bzrlib._patiencediff_c')
 
34
from 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
58
43
 
59
44
 
60
45
def udiff_lines(old, new, allow_binary=False):
80
65
    return lines
81
66
 
82
67
 
 
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
 
83
91
class TestDiff(tests.TestCase):
84
92
 
85
93
    def test_add_nl(self):
86
94
        """diff generates a valid diff for patches that add a newline"""
87
95
        lines = udiff_lines(['boo'], ['boo\n'])
88
96
        self.check_patch(lines)
89
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
97
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
90
98
            ## "expected no-nl, got %r" % lines[4]
91
99
 
92
100
    def test_add_nl_2(self):
95
103
        """
96
104
        lines = udiff_lines(['boo'], ['goo\n'])
97
105
        self.check_patch(lines)
98
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
106
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
99
107
            ## "expected no-nl, got %r" % lines[4]
100
108
 
101
109
    def test_remove_nl(self):
104
112
        """
105
113
        lines = udiff_lines(['boo\n'], ['boo'])
106
114
        self.check_patch(lines)
107
 
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
 
115
        self.assertEqual(lines[5], '\\ No newline at end of file\n')
108
116
            ## "expected no-nl, got %r" % lines[5]
109
117
 
110
118
    def check_patch(self, lines):
111
 
        self.assert_(len(lines) > 1)
 
119
        self.assertTrue(len(lines) > 1)
112
120
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
113
 
        self.assert_(lines[0].startswith ('---'))
 
121
        self.assertTrue(lines[0].startswith ('---'))
114
122
            ## 'No orig line for patch:\n%s' % "".join(lines)
115
 
        self.assert_(lines[1].startswith ('+++'))
 
123
        self.assertTrue(lines[1].startswith ('+++'))
116
124
            ## 'No mod line for patch:\n%s' % "".join(lines)
117
 
        self.assert_(len(lines) > 2)
 
125
        self.assertTrue(len(lines) > 2)
118
126
            ## "No hunks for patch:\n%s" % "".join(lines)
119
 
        self.assert_(lines[2].startswith('@@'))
 
127
        self.assertTrue(lines[2].startswith('@@'))
120
128
            ## "No hunk header for patch:\n%s" % "".join(lines)
121
 
        self.assert_('@@' in lines[2][2:])
 
129
        self.assertTrue('@@' in lines[2][2:])
122
130
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
123
131
 
124
132
    def test_binary_lines(self):
143
151
        self.check_patch(lines)
144
152
 
145
153
    def test_external_diff_binary_lang_c(self):
146
 
        old_env = {}
147
154
        for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
 
            old_env[lang] = osutils.set_or_unset_env(lang, 'C')
149
 
        try:
150
 
            lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
151
 
            # Older versions of diffutils say "Binary files", newer
152
 
            # versions just say "Files".
153
 
            self.assertContainsRe(lines[0],
154
 
                                  '(Binary f|F)iles old and new differ\n')
155
 
            self.assertEquals(lines[1:], ['\n'])
156
 
        finally:
157
 
            for lang, old_val in old_env.iteritems():
158
 
                osutils.set_or_unset_env(lang, old_val)
 
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'])
159
161
 
160
162
    def test_no_external_diff(self):
161
163
        """Check that NoDiff is raised when diff is not available"""
162
 
        # Use os.environ['PATH'] to make sure no 'diff' command is available
163
 
        orig_path = os.environ['PATH']
164
 
        try:
165
 
            os.environ['PATH'] = ''
166
 
            self.assertRaises(errors.NoDiff, diff.external_diff,
167
 
                              'old', ['boo\n'], 'new', ['goo\n'],
168
 
                              StringIO(), diff_opts=['-u'])
169
 
        finally:
170
 
            os.environ['PATH'] = orig_path
 
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'])
171
170
 
172
171
    def test_internal_diff_default(self):
173
172
        # Default internal diff encoding is utf8
176
175
                           u'new_\xe5', ['new_text\n'], output)
177
176
        lines = output.getvalue().splitlines(True)
178
177
        self.check_patch(lines)
179
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
178
        self.assertEqual(['--- old_\xc2\xb5\n',
180
179
                           '+++ new_\xc3\xa5\n',
181
180
                           '@@ -1,1 +1,1 @@\n',
182
181
                           '-old_text\n',
192
191
                           path_encoding='utf8')
193
192
        lines = output.getvalue().splitlines(True)
194
193
        self.check_patch(lines)
195
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
194
        self.assertEqual(['--- old_\xc2\xb5\n',
196
195
                           '+++ new_\xc3\xa5\n',
197
196
                           '@@ -1,1 +1,1 @@\n',
198
197
                           '-old_text\n',
208
207
                           path_encoding='iso-8859-1')
209
208
        lines = output.getvalue().splitlines(True)
210
209
        self.check_patch(lines)
211
 
        self.assertEquals(['--- old_\xb5\n',
 
210
        self.assertEqual(['--- old_\xb5\n',
212
211
                           '+++ new_\xe5\n',
213
212
                           '@@ -1,1 +1,1 @@\n',
214
213
                           '-old_text\n',
234
233
        output = StringIO.StringIO()
235
234
        diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
235
                            u'new_\xe5', ['new_text\n'], output)
237
 
        self.failUnless(isinstance(output.getvalue(), str),
 
236
        self.assertIsInstance(output.getvalue(), str,
238
237
            'internal_diff should return bytestrings')
239
238
 
 
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
 
240
302
 
241
303
class TestDiffFiles(tests.TestCaseInTempDir):
242
304
 
246
308
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
247
309
 
248
310
        cmd = ['diff', '-u', '--binary', 'old', 'new']
249
 
        open('old', 'wb').write('\x00foobar\n')
250
 
        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')
251
313
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
314
                                     stdin=subprocess.PIPE)
253
315
        out, err = pipe.communicate()
254
 
        # Diff returns '2' on Binary files.
255
 
        self.assertEqual(2, pipe.returncode)
256
316
        # We should output whatever diff tells us, plus a trailing newline
257
317
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
258
318
 
259
319
 
260
 
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
261
 
    """Has a helper for running show_diff_trees"""
262
 
 
263
 
    def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
264
 
        output = StringIO()
265
 
        if working_tree is not None:
266
 
            extra_trees = (working_tree,)
267
 
        else:
268
 
            extra_trees = ()
269
 
        diff.show_diff_trees(tree1, tree2, output,
270
 
                             specific_files=specific_files,
271
 
                             extra_trees=extra_trees, old_label='old/',
272
 
                             new_label='new/')
273
 
        return output.getvalue()
274
 
 
275
 
 
276
 
class TestDiffDates(TestShowDiffTreesHelper):
 
320
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
 
321
    output = StringIO()
 
322
    if working_tree is not None:
 
323
        extra_trees = (working_tree,)
 
324
    else:
 
325
        extra_trees = ()
 
326
    diff.show_diff_trees(tree1, tree2, output,
 
327
        specific_files=specific_files,
 
328
        extra_trees=extra_trees, old_label='old/',
 
329
        new_label='new/')
 
330
    return output.getvalue()
 
331
 
 
332
 
 
333
class TestDiffDates(tests.TestCaseWithTransport):
277
334
 
278
335
    def setUp(self):
279
336
        super(TestDiffDates, self).setUp()
314
371
        os.utime('file1', (1144195200, 1144195200)) # 2006-04-05 00:00:00 UTC
315
372
 
316
373
    def test_diff_rev_tree_working_tree(self):
317
 
        output = self.get_diff(self.wt.basis_tree(), self.wt)
 
374
        output = get_diff_as_string(self.wt.basis_tree(), self.wt)
318
375
        # note that the date for old/file1 is from rev 2 rather than from
319
376
        # the basis revision (rev 4)
320
377
        self.assertEqualDiff(output, '''\
330
387
    def test_diff_rev_tree_rev_tree(self):
331
388
        tree1 = self.b.repository.revision_tree('rev-2')
332
389
        tree2 = self.b.repository.revision_tree('rev-3')
333
 
        output = self.get_diff(tree1, tree2)
 
390
        output = get_diff_as_string(tree1, tree2)
334
391
        self.assertEqualDiff(output, '''\
335
392
=== modified file 'file2'
336
393
--- old/file2\t2006-04-01 00:00:00 +0000
344
401
    def test_diff_add_files(self):
345
402
        tree1 = self.b.repository.revision_tree(_mod_revision.NULL_REVISION)
346
403
        tree2 = self.b.repository.revision_tree('rev-1')
347
 
        output = self.get_diff(tree1, tree2)
 
404
        output = get_diff_as_string(tree1, tree2)
348
405
        # the files have the epoch time stamp for the tree in which
349
406
        # they don't exist.
350
407
        self.assertEqualDiff(output, '''\
365
422
    def test_diff_remove_files(self):
366
423
        tree1 = self.b.repository.revision_tree('rev-3')
367
424
        tree2 = self.b.repository.revision_tree('rev-4')
368
 
        output = self.get_diff(tree1, tree2)
 
425
        output = get_diff_as_string(tree1, tree2)
369
426
        # the file has the epoch time stamp for the tree in which
370
427
        # it doesn't exist.
371
428
        self.assertEqualDiff(output, '''\
382
439
        self.wt.rename_one('file1', 'file1b')
383
440
        old_tree = self.b.repository.revision_tree('rev-1')
384
441
        new_tree = self.b.repository.revision_tree('rev-4')
385
 
        out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
 
442
        out = get_diff_as_string(old_tree, new_tree, specific_files=['file1b'],
386
443
                            working_tree=self.wt)
387
444
        self.assertContainsRe(out, 'file1\t')
388
445
 
394
451
        self.wt.rename_one('file1', 'dir1/file1')
395
452
        old_tree = self.b.repository.revision_tree('rev-1')
396
453
        new_tree = self.b.repository.revision_tree('rev-4')
397
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
 
454
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
398
455
                            working_tree=self.wt)
399
456
        self.assertContainsRe(out, 'file1\t')
400
 
        out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
 
457
        out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
401
458
                            working_tree=self.wt)
402
459
        self.assertNotContainsRe(out, 'file1\t')
403
460
 
404
461
 
405
 
 
406
 
class TestShowDiffTrees(TestShowDiffTreesHelper):
 
462
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
463
    """Direct tests for show_diff_trees"""
408
464
 
409
465
    def test_modified_file(self):
414
470
        tree.commit('one', rev_id='rev-1')
415
471
 
416
472
        self.build_tree_contents([('tree/file', 'new contents\n')])
417
 
        d = self.get_diff(tree.basis_tree(), tree)
 
473
        d = get_diff_as_string(tree.basis_tree(), tree)
418
474
        self.assertContainsRe(d, "=== modified file 'file'\n")
419
475
        self.assertContainsRe(d, '--- old/file\t')
420
476
        self.assertContainsRe(d, '\\+\\+\\+ new/file\t')
431
487
 
432
488
        tree.rename_one('dir', 'other')
433
489
        self.build_tree_contents([('tree/other/file', 'new contents\n')])
434
 
        d = self.get_diff(tree.basis_tree(), tree)
 
490
        d = get_diff_as_string(tree.basis_tree(), tree)
435
491
        self.assertContainsRe(d, "=== renamed directory 'dir' => 'other'\n")
436
492
        self.assertContainsRe(d, "=== modified file 'other/file'\n")
437
493
        # XXX: This is technically incorrect, because it used to be at another
450
506
        tree.commit('one', rev_id='rev-1')
451
507
 
452
508
        tree.rename_one('dir', 'newdir')
453
 
        d = self.get_diff(tree.basis_tree(), tree)
 
509
        d = get_diff_as_string(tree.basis_tree(), tree)
454
510
        # Renaming a directory should be a single "you renamed this dir" even
455
511
        # when there are files inside.
456
512
        self.assertEqual(d, "=== renamed directory 'dir' => 'newdir'\n")
463
519
        tree.commit('one', rev_id='rev-1')
464
520
 
465
521
        tree.rename_one('file', 'newname')
466
 
        d = self.get_diff(tree.basis_tree(), tree)
 
522
        d = get_diff_as_string(tree.basis_tree(), tree)
467
523
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
468
524
        # We shouldn't have a --- or +++ line, because there is no content
469
525
        # change
478
534
 
479
535
        tree.rename_one('file', 'newname')
480
536
        self.build_tree_contents([('tree/newname', 'new contents\n')])
481
 
        d = self.get_diff(tree.basis_tree(), tree)
 
537
        d = get_diff_as_string(tree.basis_tree(), tree)
482
538
        self.assertContainsRe(d, "=== renamed file 'file' => 'newname'\n")
483
539
        self.assertContainsRe(d, '--- old/file\t')
484
540
        self.assertContainsRe(d, '\\+\\+\\+ new/newname\t')
508
564
        tree.rename_one('c', 'new-c')
509
565
        tree.rename_one('d', 'new-d')
510
566
 
511
 
        d = self.get_diff(tree.basis_tree(), tree)
 
567
        d = get_diff_as_string(tree.basis_tree(), tree)
512
568
 
513
569
        self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
514
570
                                  ".*\+x to -x.*\)")
521
577
        self.assertNotContainsRe(d, r"file 'e'")
522
578
        self.assertNotContainsRe(d, r"file 'f'")
523
579
 
524
 
 
525
580
    def test_binary_unicode_filenames(self):
526
581
        """Test that contents of files are *not* encoded in UTF-8 when there
527
582
        is a binary file in the diff.
528
583
        """
529
584
        # See https://bugs.launchpad.net/bugs/110092.
530
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
585
        self.requireFeature(features.UnicodeFilenameFeature)
531
586
 
532
587
        # This bug isn't triggered with cStringIO.
533
588
        from StringIO import StringIO
552
607
 
553
608
    def test_unicode_filename(self):
554
609
        """Test when the filename are unicode."""
555
 
        self.requireFeature(tests.UnicodeFilenameFeature)
 
610
        self.requireFeature(features.UnicodeFilenameFeature)
556
611
 
557
612
        alpha, omega = u'\u03b1', u'\u03c9'
558
613
        autf8, outf8 = alpha.encode('utf8'), omega.encode('utf8')
573
628
        tree.add(['add_'+alpha], ['file-id'])
574
629
        self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
575
630
 
576
 
        d = self.get_diff(tree.basis_tree(), tree)
 
631
        d = get_diff_as_string(tree.basis_tree(), tree)
577
632
        self.assertContainsRe(d,
578
633
                "=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
634
        self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
635
        self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
636
        self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
582
637
 
 
638
    def test_unicode_filename_path_encoding(self):
 
639
        """Test for bug #382699: unicode filenames on Windows should be shown
 
640
        in user encoding.
 
641
        """
 
642
        self.requireFeature(features.UnicodeFilenameFeature)
 
643
        # The word 'test' in Russian
 
644
        _russian_test = u'\u0422\u0435\u0441\u0442'
 
645
        directory = _russian_test + u'/'
 
646
        test_txt = _russian_test + u'.txt'
 
647
        u1234 = u'\u1234.txt'
 
648
 
 
649
        tree = self.make_branch_and_tree('.')
 
650
        self.build_tree_contents([
 
651
            (test_txt, 'foo\n'),
 
652
            (u1234, 'foo\n'),
 
653
            (directory, None),
 
654
            ])
 
655
        tree.add([test_txt, u1234, directory])
 
656
 
 
657
        sio = StringIO()
 
658
        diff.show_diff_trees(tree.basis_tree(), tree, sio,
 
659
            path_encoding='cp1251')
 
660
 
 
661
        output = subst_dates(sio.getvalue())
 
662
        shouldbe = ('''\
 
663
=== added directory '%(directory)s'
 
664
=== added file '%(test_txt)s'
 
665
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
666
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
667
@@ -0,0 +1,1 @@
 
668
+foo
 
669
 
 
670
=== added file '?.txt'
 
671
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
672
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
 
673
@@ -0,0 +1,1 @@
 
674
+foo
 
675
 
 
676
''' % {'directory': _russian_test.encode('cp1251'),
 
677
       'test_txt': test_txt.encode('cp1251'),
 
678
      })
 
679
        self.assertEqualDiff(output, shouldbe)
 
680
 
583
681
 
584
682
class DiffWasIs(diff.DiffPath):
585
683
 
678
776
             ' \@\@\n-old\n\+new\n\n')
679
777
 
680
778
    def test_diff_kind_change(self):
681
 
        self.requireFeature(tests.SymlinkFeature)
 
779
        self.requireFeature(features.SymlinkFeature)
682
780
        self.build_tree_contents([('old-tree/olddir/',),
683
781
                                  ('old-tree/olddir/oldfile', 'old\n')])
684
782
        self.old_tree.add('olddir')
764
862
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
863
        sm = self._PatienceSequenceMatcher(None, a, b)
766
864
        mb = sm.get_matching_blocks()
767
 
        self.assertEquals(35, len(mb))
 
865
        self.assertEqual(35, len(mb))
768
866
 
769
867
    def test_unique_lcs(self):
770
868
        unique_lcs = self._unique_lcs
771
 
        self.assertEquals(unique_lcs('', ''), [])
772
 
        self.assertEquals(unique_lcs('', 'a'), [])
773
 
        self.assertEquals(unique_lcs('a', ''), [])
774
 
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
775
 
        self.assertEquals(unique_lcs('a', 'b'), [])
776
 
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
777
 
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
778
 
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
779
 
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
 
869
        self.assertEqual(unique_lcs('', ''), [])
 
870
        self.assertEqual(unique_lcs('', 'a'), [])
 
871
        self.assertEqual(unique_lcs('a', ''), [])
 
872
        self.assertEqual(unique_lcs('a', 'a'), [(0,0)])
 
873
        self.assertEqual(unique_lcs('a', 'b'), [])
 
874
        self.assertEqual(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
 
875
        self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
 
876
        self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
 
877
        self.assertEqual(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
780
878
                                                         (3,3), (4,4)])
781
 
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
 
879
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2,1)])
782
880
 
783
881
    def test_recurse_matches(self):
784
882
        def test_one(a, b, matches):
785
883
            test_matches = []
786
884
            self._recurse_matches(
787
885
                a, b, 0, 0, len(a), len(b), test_matches, 10)
788
 
            self.assertEquals(test_matches, matches)
 
886
            self.assertEqual(test_matches, matches)
789
887
 
790
888
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
889
                 [(0, 0), (2, 2), (4, 4)])
896
994
    def test_opcodes(self):
897
995
        def chk_ops(a, b, expected_codes):
898
996
            s = self._PatienceSequenceMatcher(None, a, b)
899
 
            self.assertEquals(expected_codes, s.get_opcodes())
 
997
            self.assertEqual(expected_codes, s.get_opcodes())
900
998
 
901
999
        chk_ops('', '', [])
902
1000
        chk_ops([], [], [])
972
1070
    def test_grouped_opcodes(self):
973
1071
        def chk_ops(a, b, expected_codes, n=3):
974
1072
            s = self._PatienceSequenceMatcher(None, a, b)
975
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
 
1073
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
976
1074
 
977
1075
        chk_ops('', '', [])
978
1076
        chk_ops([], [], [])
1072
1170
                 'how are you today?\n']
1073
1171
        unified_diff = patiencediff.unified_diff
1074
1172
        psm = self._PatienceSequenceMatcher
1075
 
        self.assertEquals(['--- \n',
 
1173
        self.assertEqual(['--- \n',
1076
1174
                           '+++ \n',
1077
1175
                           '@@ -1,3 +1,2 @@\n',
1078
1176
                           ' hello there\n',
1084
1182
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1085
1183
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1086
1184
        # This is the result with LongestCommonSubstring matching
1087
 
        self.assertEquals(['--- \n',
 
1185
        self.assertEqual(['--- \n',
1088
1186
                           '+++ \n',
1089
1187
                           '@@ -1,6 +1,11 @@\n',
1090
1188
                           ' a\n',
1100
1198
                           ' f\n']
1101
1199
                          , list(unified_diff(txt_a, txt_b)))
1102
1200
        # And the patience diff
1103
 
        self.assertEquals(['--- \n',
 
1201
        self.assertEqual(['--- \n',
1104
1202
                           '+++ \n',
1105
1203
                           '@@ -4,6 +4,11 @@\n',
1106
1204
                           ' d\n',
1126
1224
                 'how are you today?\n']
1127
1225
        unified_diff = patiencediff.unified_diff
1128
1226
        psm = self._PatienceSequenceMatcher
1129
 
        self.assertEquals(['--- a\t2008-08-08\n',
 
1227
        self.assertEqual(['--- a\t2008-08-08\n',
1130
1228
                           '+++ b\t2008-09-09\n',
1131
1229
                           '@@ -1,3 +1,2 @@\n',
1132
1230
                           ' hello there\n',
1142
1240
 
1143
1241
class TestPatienceDiffLib_c(TestPatienceDiffLib):
1144
1242
 
1145
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1243
    _test_needs_features = [features.compiled_patiencediff_feature]
1146
1244
 
1147
1245
    def setUp(self):
1148
1246
        super(TestPatienceDiffLib_c, self).setUp()
1179
1277
                 'how are you today?\n']
1180
1278
        txt_b = ['hello there\n',
1181
1279
                 'how are you today?\n']
1182
 
        open('a1', 'wb').writelines(txt_a)
1183
 
        open('b1', 'wb').writelines(txt_b)
 
1280
        with open('a1', 'wb') as f: f.writelines(txt_a)
 
1281
        with open('b1', 'wb') as f: f.writelines(txt_b)
1184
1282
 
1185
1283
        unified_diff_files = patiencediff.unified_diff_files
1186
1284
        psm = self._PatienceSequenceMatcher
1187
 
        self.assertEquals(['--- a1\n',
 
1285
        self.assertEqual(['--- a1\n',
1188
1286
                           '+++ b1\n',
1189
1287
                           '@@ -1,3 +1,2 @@\n',
1190
1288
                           ' hello there\n',
1196
1294
 
1197
1295
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1198
1296
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1199
 
        open('a2', 'wb').writelines(txt_a)
1200
 
        open('b2', 'wb').writelines(txt_b)
 
1297
        with open('a2', 'wb') as f: f.writelines(txt_a)
 
1298
        with open('b2', 'wb') as f: f.writelines(txt_b)
1201
1299
 
1202
1300
        # This is the result with LongestCommonSubstring matching
1203
 
        self.assertEquals(['--- a2\n',
 
1301
        self.assertEqual(['--- a2\n',
1204
1302
                           '+++ b2\n',
1205
1303
                           '@@ -1,6 +1,11 @@\n',
1206
1304
                           ' a\n',
1217
1315
                          , list(unified_diff_files('a2', 'b2')))
1218
1316
 
1219
1317
        # And the patience diff
1220
 
        self.assertEquals(['--- a2\n',
1221
 
                           '+++ b2\n',
1222
 
                           '@@ -4,6 +4,11 @@\n',
1223
 
                           ' d\n',
1224
 
                           ' e\n',
1225
 
                           ' f\n',
1226
 
                           '+x\n',
1227
 
                           '+y\n',
1228
 
                           '+d\n',
1229
 
                           '+e\n',
1230
 
                           '+f\n',
1231
 
                           ' g\n',
1232
 
                           ' h\n',
1233
 
                           ' i\n',
1234
 
                          ]
1235
 
                          , list(unified_diff_files('a2', 'b2',
1236
 
                                 sequencematcher=psm)))
 
1318
        self.assertEqual(['--- a2\n',
 
1319
                          '+++ b2\n',
 
1320
                          '@@ -4,6 +4,11 @@\n',
 
1321
                          ' d\n',
 
1322
                          ' e\n',
 
1323
                          ' f\n',
 
1324
                          '+x\n',
 
1325
                          '+y\n',
 
1326
                          '+d\n',
 
1327
                          '+e\n',
 
1328
                          '+f\n',
 
1329
                          ' g\n',
 
1330
                          ' h\n',
 
1331
                          ' i\n'],
 
1332
                         list(unified_diff_files('a2', 'b2',
 
1333
                                                 sequencematcher=psm)))
1237
1334
 
1238
1335
 
1239
1336
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1240
1337
 
1241
 
    _test_needs_features = [compiled_patiencediff_feature]
 
1338
    _test_needs_features = [features.compiled_patiencediff_feature]
1242
1339
 
1243
1340
    def setUp(self):
1244
1341
        super(TestPatienceDiffLibFiles_c, self).setUp()
1250
1347
class TestUsingCompiledIfAvailable(tests.TestCase):
1251
1348
 
1252
1349
    def test_PatienceSequenceMatcher(self):
1253
 
        if compiled_patiencediff_feature.available():
 
1350
        if features.compiled_patiencediff_feature.available():
1254
1351
            from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1255
1352
            self.assertIs(PatienceSequenceMatcher_c,
1256
1353
                          patiencediff.PatienceSequenceMatcher)
1260
1357
                          patiencediff.PatienceSequenceMatcher)
1261
1358
 
1262
1359
    def test_unique_lcs(self):
1263
 
        if compiled_patiencediff_feature.available():
 
1360
        if features.compiled_patiencediff_feature.available():
1264
1361
            from bzrlib._patiencediff_c import unique_lcs_c
1265
1362
            self.assertIs(unique_lcs_c,
1266
1363
                          patiencediff.unique_lcs)
1270
1367
                          patiencediff.unique_lcs)
1271
1368
 
1272
1369
    def test_recurse_matches(self):
1273
 
        if compiled_patiencediff_feature.available():
 
1370
        if features.compiled_patiencediff_feature.available():
1274
1371
            from bzrlib._patiencediff_c import recurse_matches_c
1275
1372
            self.assertIs(recurse_matches_c,
1276
1373
                          patiencediff.recurse_matches)
1298
1395
                         diff_obj._get_command('old-path', 'new-path'))
1299
1396
 
1300
1397
    def test_from_string_path_with_backslashes(self):
1301
 
        self.requireFeature(test_win32utils.BackslashDirSeparatorFeature)
 
1398
        self.requireFeature(features.backslashdir_feature)
1302
1399
        tool = 'C:\\Tools\\Diff.exe'
1303
1400
        diff_obj = diff.DiffFromTool.from_string(tool, None, None, None)
1304
1401
        self.addCleanup(diff_obj.finish)
1316
1413
        diff_obj._execute('old', 'new')
1317
1414
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1318
1415
 
1319
 
    def test_excute_missing(self):
 
1416
    def test_execute_missing(self):
1320
1417
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1321
1418
                                     None, None, None)
1322
1419
        self.addCleanup(diff_obj.finish)
1326
1423
                         ' on this machine', str(e))
1327
1424
 
1328
1425
    def test_prepare_files_creates_paths_readable_by_windows_tool(self):
1329
 
        self.requireFeature(AttribFeature)
 
1426
        self.requireFeature(features.AttribFeature)
1330
1427
        output = StringIO()
1331
1428
        tree = self.make_branch_and_tree('tree')
1332
1429
        self.build_tree_contents([('tree/file', 'content')])
1391
1488
        diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1392
1489
 
1393
1490
 
 
1491
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
 
1492
 
 
1493
    def test_encodable_filename(self):
 
1494
        # Just checks file path for external diff tool.
 
1495
        # We cannot change CPython's internal encoding used by os.exec*.
 
1496
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1497
                                    None, None, None)
 
1498
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1499
            encoding = scenario['encoding']
 
1500
            dirname = scenario['info']['directory']
 
1501
            filename = scenario['info']['filename']
 
1502
 
 
1503
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1504
            relpath = dirname + u'/' + filename
 
1505
            fullpath = diffobj._safe_filename('safe', relpath)
 
1506
            self.assertEqual(fullpath,
 
1507
                             fullpath.encode(encoding).decode(encoding))
 
1508
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
 
1509
 
 
1510
    def test_unencodable_filename(self):
 
1511
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
 
1512
                                    None, None, None)
 
1513
        for _, scenario in EncodingAdapter.encoding_scenarios:
 
1514
            encoding = scenario['encoding']
 
1515
            dirname = scenario['info']['directory']
 
1516
            filename = scenario['info']['filename']
 
1517
 
 
1518
            if encoding == 'iso-8859-1':
 
1519
                encoding = 'iso-8859-2'
 
1520
            else:
 
1521
                encoding = 'iso-8859-1'
 
1522
 
 
1523
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
 
1524
            relpath = dirname + u'/' + filename
 
1525
            fullpath = diffobj._safe_filename('safe', relpath)
 
1526
            self.assertEqual(fullpath,
 
1527
                             fullpath.encode(encoding).decode(encoding))
 
1528
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
 
1529
 
 
1530
 
1394
1531
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1395
1532
 
1396
1533
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1397
 
        """Call get_trees_and_branches_to_diff_locked.  Overridden by
1398
 
        TestGetTreesAndBranchesToDiff.
1399
 
        """
 
1534
        """Call get_trees_and_branches_to_diff_locked."""
1400
1535
        return diff.get_trees_and_branches_to_diff_locked(
1401
1536
            path_list, revision_specs, old_url, new_url, self.addCleanup)
1402
1537
 
1439
1574
        self.assertEqual(tree.branch.base, new_branch.base)
1440
1575
        self.assertIs(None, specific_files)
1441
1576
        self.assertEqual(tree.basedir, extra_trees[0].basedir)
1442
 
 
1443
 
 
1444
 
class TestGetTreesAndBranchesToDiff(TestGetTreesAndBranchesToDiffLocked):
1445
 
    """Apply the tests for get_trees_and_branches_to_diff_locked to the
1446
 
    deprecated get_trees_and_branches_to_diff function.
1447
 
    """
1448
 
 
1449
 
    def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1450
 
        return self.applyDeprecated(
1451
 
            deprecated_in((2, 2, 0)), diff.get_trees_and_branches_to_diff,
1452
 
            path_list, revision_specs, old_url, new_url)
1453