~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_diff.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 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 features, EncodingAdapter
37
 
from bzrlib.tests.blackbox.test_diff import subst_dates
38
34
from bzrlib.tests import (
39
35
    features,
40
 
    )
 
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
41
43
 
42
44
 
43
45
def udiff_lines(old, new, allow_binary=False):
63
65
    return lines
64
66
 
65
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
 
66
91
class TestDiff(tests.TestCase):
67
92
 
68
93
    def test_add_nl(self):
69
94
        """diff generates a valid diff for patches that add a newline"""
70
95
        lines = udiff_lines(['boo'], ['boo\n'])
71
96
        self.check_patch(lines)
72
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
97
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
73
98
            ## "expected no-nl, got %r" % lines[4]
74
99
 
75
100
    def test_add_nl_2(self):
78
103
        """
79
104
        lines = udiff_lines(['boo'], ['goo\n'])
80
105
        self.check_patch(lines)
81
 
        self.assertEquals(lines[4], '\\ No newline at end of file\n')
 
106
        self.assertEqual(lines[4], '\\ No newline at end of file\n')
82
107
            ## "expected no-nl, got %r" % lines[4]
83
108
 
84
109
    def test_remove_nl(self):
87
112
        """
88
113
        lines = udiff_lines(['boo\n'], ['boo'])
89
114
        self.check_patch(lines)
90
 
        self.assertEquals(lines[5], '\\ No newline at end of file\n')
 
115
        self.assertEqual(lines[5], '\\ No newline at end of file\n')
91
116
            ## "expected no-nl, got %r" % lines[5]
92
117
 
93
118
    def check_patch(self, lines):
94
 
        self.assert_(len(lines) > 1)
 
119
        self.assertTrue(len(lines) > 1)
95
120
            ## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
96
 
        self.assert_(lines[0].startswith ('---'))
 
121
        self.assertTrue(lines[0].startswith ('---'))
97
122
            ## 'No orig line for patch:\n%s' % "".join(lines)
98
 
        self.assert_(lines[1].startswith ('+++'))
 
123
        self.assertTrue(lines[1].startswith ('+++'))
99
124
            ## 'No mod line for patch:\n%s' % "".join(lines)
100
 
        self.assert_(len(lines) > 2)
 
125
        self.assertTrue(len(lines) > 2)
101
126
            ## "No hunks for patch:\n%s" % "".join(lines)
102
 
        self.assert_(lines[2].startswith('@@'))
 
127
        self.assertTrue(lines[2].startswith('@@'))
103
128
            ## "No hunk header for patch:\n%s" % "".join(lines)
104
 
        self.assert_('@@' in lines[2][2:])
 
129
        self.assertTrue('@@' in lines[2][2:])
105
130
            ## "Unterminated hunk header for patch:\n%s" % "".join(lines)
106
131
 
107
132
    def test_binary_lines(self):
132
157
        # Older versions of diffutils say "Binary files", newer
133
158
        # versions just say "Files".
134
159
        self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
135
 
        self.assertEquals(lines[1:], ['\n'])
 
160
        self.assertEqual(lines[1:], ['\n'])
136
161
 
137
162
    def test_no_external_diff(self):
138
163
        """Check that NoDiff is raised when diff is not available"""
150
175
                           u'new_\xe5', ['new_text\n'], output)
151
176
        lines = output.getvalue().splitlines(True)
152
177
        self.check_patch(lines)
153
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
178
        self.assertEqual(['--- old_\xc2\xb5\n',
154
179
                           '+++ new_\xc3\xa5\n',
155
180
                           '@@ -1,1 +1,1 @@\n',
156
181
                           '-old_text\n',
166
191
                           path_encoding='utf8')
167
192
        lines = output.getvalue().splitlines(True)
168
193
        self.check_patch(lines)
169
 
        self.assertEquals(['--- old_\xc2\xb5\n',
 
194
        self.assertEqual(['--- old_\xc2\xb5\n',
170
195
                           '+++ new_\xc3\xa5\n',
171
196
                           '@@ -1,1 +1,1 @@\n',
172
197
                           '-old_text\n',
182
207
                           path_encoding='iso-8859-1')
183
208
        lines = output.getvalue().splitlines(True)
184
209
        self.check_patch(lines)
185
 
        self.assertEquals(['--- old_\xb5\n',
 
210
        self.assertEqual(['--- old_\xb5\n',
186
211
                           '+++ new_\xe5\n',
187
212
                           '@@ -1,1 +1,1 @@\n',
188
213
                           '-old_text\n',
211
236
        self.assertIsInstance(output.getvalue(), str,
212
237
            'internal_diff should return bytestrings')
213
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
 
214
302
 
215
303
class TestDiffFiles(tests.TestCaseInTempDir):
216
304
 
220
308
        lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
221
309
 
222
310
        cmd = ['diff', '-u', '--binary', 'old', 'new']
223
 
        open('old', 'wb').write('\x00foobar\n')
224
 
        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')
225
313
        pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
226
314
                                     stdin=subprocess.PIPE)
227
315
        out, err = pipe.communicate()
228
 
        # Diff returns '2' on Binary files.
229
 
        self.assertEqual(2, pipe.returncode)
230
316
        # We should output whatever diff tells us, plus a trailing newline
231
317
        self.assertEqual(out.splitlines(True) + ['\n'], lines)
232
318
 
776
862
        b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
777
863
        sm = self._PatienceSequenceMatcher(None, a, b)
778
864
        mb = sm.get_matching_blocks()
779
 
        self.assertEquals(35, len(mb))
 
865
        self.assertEqual(35, len(mb))
780
866
 
781
867
    def test_unique_lcs(self):
782
868
        unique_lcs = self._unique_lcs
783
 
        self.assertEquals(unique_lcs('', ''), [])
784
 
        self.assertEquals(unique_lcs('', 'a'), [])
785
 
        self.assertEquals(unique_lcs('a', ''), [])
786
 
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
787
 
        self.assertEquals(unique_lcs('a', 'b'), [])
788
 
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
789
 
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
790
 
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
791
 
        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),
792
878
                                                         (3,3), (4,4)])
793
 
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
 
879
        self.assertEqual(unique_lcs('acbac', 'abc'), [(2,1)])
794
880
 
795
881
    def test_recurse_matches(self):
796
882
        def test_one(a, b, matches):
797
883
            test_matches = []
798
884
            self._recurse_matches(
799
885
                a, b, 0, 0, len(a), len(b), test_matches, 10)
800
 
            self.assertEquals(test_matches, matches)
 
886
            self.assertEqual(test_matches, matches)
801
887
 
802
888
        test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
803
889
                 [(0, 0), (2, 2), (4, 4)])
908
994
    def test_opcodes(self):
909
995
        def chk_ops(a, b, expected_codes):
910
996
            s = self._PatienceSequenceMatcher(None, a, b)
911
 
            self.assertEquals(expected_codes, s.get_opcodes())
 
997
            self.assertEqual(expected_codes, s.get_opcodes())
912
998
 
913
999
        chk_ops('', '', [])
914
1000
        chk_ops([], [], [])
984
1070
    def test_grouped_opcodes(self):
985
1071
        def chk_ops(a, b, expected_codes, n=3):
986
1072
            s = self._PatienceSequenceMatcher(None, a, b)
987
 
            self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
 
1073
            self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
988
1074
 
989
1075
        chk_ops('', '', [])
990
1076
        chk_ops([], [], [])
1084
1170
                 'how are you today?\n']
1085
1171
        unified_diff = patiencediff.unified_diff
1086
1172
        psm = self._PatienceSequenceMatcher
1087
 
        self.assertEquals(['--- \n',
 
1173
        self.assertEqual(['--- \n',
1088
1174
                           '+++ \n',
1089
1175
                           '@@ -1,3 +1,2 @@\n',
1090
1176
                           ' hello there\n',
1096
1182
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1097
1183
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1098
1184
        # This is the result with LongestCommonSubstring matching
1099
 
        self.assertEquals(['--- \n',
 
1185
        self.assertEqual(['--- \n',
1100
1186
                           '+++ \n',
1101
1187
                           '@@ -1,6 +1,11 @@\n',
1102
1188
                           ' a\n',
1112
1198
                           ' f\n']
1113
1199
                          , list(unified_diff(txt_a, txt_b)))
1114
1200
        # And the patience diff
1115
 
        self.assertEquals(['--- \n',
 
1201
        self.assertEqual(['--- \n',
1116
1202
                           '+++ \n',
1117
1203
                           '@@ -4,6 +4,11 @@\n',
1118
1204
                           ' d\n',
1138
1224
                 'how are you today?\n']
1139
1225
        unified_diff = patiencediff.unified_diff
1140
1226
        psm = self._PatienceSequenceMatcher
1141
 
        self.assertEquals(['--- a\t2008-08-08\n',
 
1227
        self.assertEqual(['--- a\t2008-08-08\n',
1142
1228
                           '+++ b\t2008-09-09\n',
1143
1229
                           '@@ -1,3 +1,2 @@\n',
1144
1230
                           ' hello there\n',
1191
1277
                 'how are you today?\n']
1192
1278
        txt_b = ['hello there\n',
1193
1279
                 'how are you today?\n']
1194
 
        open('a1', 'wb').writelines(txt_a)
1195
 
        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)
1196
1282
 
1197
1283
        unified_diff_files = patiencediff.unified_diff_files
1198
1284
        psm = self._PatienceSequenceMatcher
1199
 
        self.assertEquals(['--- a1\n',
 
1285
        self.assertEqual(['--- a1\n',
1200
1286
                           '+++ b1\n',
1201
1287
                           '@@ -1,3 +1,2 @@\n',
1202
1288
                           ' hello there\n',
1208
1294
 
1209
1295
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
1210
1296
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
1211
 
        open('a2', 'wb').writelines(txt_a)
1212
 
        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)
1213
1299
 
1214
1300
        # This is the result with LongestCommonSubstring matching
1215
 
        self.assertEquals(['--- a2\n',
 
1301
        self.assertEqual(['--- a2\n',
1216
1302
                           '+++ b2\n',
1217
1303
                           '@@ -1,6 +1,11 @@\n',
1218
1304
                           ' a\n',
1229
1315
                          , list(unified_diff_files('a2', 'b2')))
1230
1316
 
1231
1317
        # And the patience diff
1232
 
        self.assertEquals(['--- a2\n',
1233
 
                           '+++ b2\n',
1234
 
                           '@@ -4,6 +4,11 @@\n',
1235
 
                           ' d\n',
1236
 
                           ' e\n',
1237
 
                           ' f\n',
1238
 
                           '+x\n',
1239
 
                           '+y\n',
1240
 
                           '+d\n',
1241
 
                           '+e\n',
1242
 
                           '+f\n',
1243
 
                           ' g\n',
1244
 
                           ' h\n',
1245
 
                           ' i\n',
1246
 
                          ]
1247
 
                          , list(unified_diff_files('a2', 'b2',
1248
 
                                 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)))
1249
1334
 
1250
1335
 
1251
1336
class TestPatienceDiffLibFiles_c(TestPatienceDiffLibFiles):
1328
1413
        diff_obj._execute('old', 'new')
1329
1414
        self.assertEqual(output.getvalue().rstrip(), 'old new')
1330
1415
 
1331
 
    def test_excute_missing(self):
 
1416
    def test_execute_missing(self):
1332
1417
        diff_obj = diff.DiffFromTool(['a-tool-which-is-unlikely-to-exist'],
1333
1418
                                     None, None, None)
1334
1419
        self.addCleanup(diff_obj.finish)
1408
1493
    def test_encodable_filename(self):
1409
1494
        # Just checks file path for external diff tool.
1410
1495
        # We cannot change CPython's internal encoding used by os.exec*.
1411
 
        import sys
1412
1496
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1413
1497
                                    None, None, None)
1414
1498
        for _, scenario in EncodingAdapter.encoding_scenarios:
1415
1499
            encoding = scenario['encoding']
1416
 
            dirname  = scenario['info']['directory']
 
1500
            dirname = scenario['info']['directory']
1417
1501
            filename = scenario['info']['filename']
1418
1502
 
1419
1503
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1420
1504
            relpath = dirname + u'/' + filename
1421
1505
            fullpath = diffobj._safe_filename('safe', relpath)
1422
 
            self.assertEqual(
1423
 
                    fullpath,
1424
 
                    fullpath.encode(encoding).decode(encoding)
1425
 
                    )
1426
 
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1506
            self.assertEqual(fullpath,
 
1507
                             fullpath.encode(encoding).decode(encoding))
 
1508
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1427
1509
 
1428
1510
    def test_unencodable_filename(self):
1429
 
        import sys
1430
1511
        diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1431
1512
                                    None, None, None)
1432
1513
        for _, scenario in EncodingAdapter.encoding_scenarios:
1433
1514
            encoding = scenario['encoding']
1434
 
            dirname  = scenario['info']['directory']
 
1515
            dirname = scenario['info']['directory']
1435
1516
            filename = scenario['info']['filename']
1436
1517
 
1437
1518
            if encoding == 'iso-8859-1':
1442
1523
            self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1443
1524
            relpath = dirname + u'/' + filename
1444
1525
            fullpath = diffobj._safe_filename('safe', relpath)
1445
 
            self.assertEqual(
1446
 
                    fullpath,
1447
 
                    fullpath.encode(encoding).decode(encoding)
1448
 
                    )
1449
 
            self.assert_(fullpath.startswith(diffobj._root + '/safe'))
 
1526
            self.assertEqual(fullpath,
 
1527
                             fullpath.encode(encoding).decode(encoding))
 
1528
            self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1450
1529
 
1451
1530
 
1452
1531
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):