~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: 2006-05-23 14:14:55 UTC
  • mfrom: (1185.81.29 bzr.patience)
  • Revision ID: pqm@pqm.ubuntu.com-20060523141455-306c645e0da6d13d
Use Patience sequence matcher for diff and merge3

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
from bzrlib.diff import internal_diff
4
4
from bzrlib.errors import BinaryFile
5
 
from bzrlib.tests import TestCase
 
5
from bzrlib.patiencediff import (recurse_matches, SequenceMatcher, unique_lcs,
 
6
                                 unified_diff, unified_diff_files)
 
7
from bzrlib.tests import TestCase, TestCaseInTempDir
6
8
 
7
9
 
8
10
def udiff_lines(old, new, allow_binary=False):
11
13
    output.seek(0, 0)
12
14
    return output.readlines()
13
15
 
 
16
 
14
17
class TestDiff(TestCase):
 
18
 
15
19
    def test_add_nl(self):
16
20
        """diff generates a valid diff for patches that add a newline"""
17
21
        lines = udiff_lines(['boo'], ['boo\n'])
56
60
        self.assertRaises(BinaryFile, udiff_lines, [], [1023 * 'a' + '\x00'])
57
61
        udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
58
62
        udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
 
63
 
 
64
 
 
65
class TestCDVDiffLib(TestCase):
 
66
 
 
67
    def test_unique_lcs(self):
 
68
        self.assertEquals(unique_lcs('', ''), [])
 
69
        self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
 
70
        self.assertEquals(unique_lcs('a', 'b'), [])
 
71
        self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
 
72
        self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
 
73
        self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
 
74
        self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1), 
 
75
                                                         (3,3), (4,4)])
 
76
        self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
 
77
 
 
78
    def test_recurse_matches(self):
 
79
        def test_one(a, b, matches):
 
80
            test_matches = []
 
81
            recurse_matches(a, b, len(a), len(b), test_matches, 10)
 
82
            self.assertEquals(test_matches, matches)
 
83
 
 
84
        test_one(['a', None, 'b', None, 'c'], ['a', 'a', 'b', 'c', 'c'],
 
85
                 [(0, 0), (2, 2), (4, 4)])
 
86
        test_one(['a', 'c', 'b', 'a', 'c'], ['a', 'b', 'c'],
 
87
                 [(0, 0), (2, 1), (4, 2)])
 
88
 
 
89
        # recurse_matches doesn't match non-unique 
 
90
        # lines surrounded by bogus text.
 
91
        # The update has been done in patiencediff.SequenceMatcher instead
 
92
 
 
93
        # This is what it could be
 
94
        #test_one('aBccDe', 'abccde', [(0,0), (2,2), (3,3), (5,5)])
 
95
 
 
96
        # This is what it currently gives:
 
97
        test_one('aBccDe', 'abccde', [(0,0), (5,5)])
 
98
 
 
99
    def test_matching_blocks(self):
 
100
        def chk_blocks(a, b, matching):
 
101
            # difflib always adds a signature of the total
 
102
            # length, with no matching entries at the end
 
103
            s = SequenceMatcher(None, a, b)
 
104
            blocks = s.get_matching_blocks()
 
105
            x = blocks.pop()
 
106
            self.assertEquals(x, (len(a), len(b), 0))
 
107
            self.assertEquals(matching, blocks)
 
108
 
 
109
        # Some basic matching tests
 
110
        chk_blocks('', '', [])
 
111
        chk_blocks([], [], [])
 
112
        chk_blocks('abcd', 'abcd', [(0, 0, 4)])
 
113
        chk_blocks('abcd', 'abce', [(0, 0, 3)])
 
114
        chk_blocks('eabc', 'abce', [(1, 0, 3)])
 
115
        chk_blocks('eabce', 'abce', [(1, 0, 4)])
 
116
        chk_blocks('abcde', 'abXde', [(0, 0, 2), (3, 3, 2)])
 
117
        chk_blocks('abcde', 'abXYZde', [(0, 0, 2), (3, 5, 2)])
 
118
        chk_blocks('abde', 'abXYZde', [(0, 0, 2), (2, 5, 2)])
 
119
        # This may check too much, but it checks to see that 
 
120
        # a copied block stays attached to the previous section,
 
121
        # not the later one.
 
122
        # difflib would tend to grab the trailing longest match
 
123
        # which would make the diff not look right
 
124
        chk_blocks('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
125
                   [(0, 0, 6), (6, 11, 10)])
 
126
 
 
127
        # make sure it supports passing in lists
 
128
        chk_blocks(
 
129
                   ['hello there\n',
 
130
                    'world\n',
 
131
                    'how are you today?\n'],
 
132
                   ['hello there\n',
 
133
                    'how are you today?\n'],
 
134
                [(0, 0, 1), (2, 1, 1)])
 
135
 
 
136
        chk_blocks('aBccDe', 'abccde', [(0,0,1), (2,2,2), (5,5,1)])
 
137
 
 
138
        chk_blocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
 
139
                                              (5,5,2), (8,8,1)])
 
140
 
 
141
        chk_blocks('abbabbXd', 'cabbabxd', [(0,1,5), (7,7,1)])
 
142
        chk_blocks('abbabbbb', 'cabbabbc', [(0,1,6)])
 
143
        chk_blocks('bbbbbbbb', 'cbbbbbbc', [(0,1,6)])
 
144
 
 
145
    def test_opcodes(self):
 
146
        def chk_ops(a, b, codes):
 
147
            s = SequenceMatcher(None, a, b)
 
148
            self.assertEquals(codes, s.get_opcodes())
 
149
 
 
150
        chk_ops('', '', [])
 
151
        chk_ops([], [], [])
 
152
        chk_ops('abcd', 'abcd', [('equal',    0,4, 0,4)])
 
153
        chk_ops('abcd', 'abce', [('equal',   0,3, 0,3),
 
154
                                 ('replace', 3,4, 3,4)
 
155
                                ])
 
156
        chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
 
157
                                 ('equal',  1,4, 0,3),
 
158
                                 ('insert', 4,4, 3,4)
 
159
                                ])
 
160
        chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
 
161
                                  ('equal',  1,5, 0,4)
 
162
                                 ])
 
163
        chk_ops('abcde', 'abXde', [('equal',   0,2, 0,2),
 
164
                                   ('replace', 2,3, 2,3),
 
165
                                   ('equal',   3,5, 3,5)
 
166
                                  ])
 
167
        chk_ops('abcde', 'abXYZde', [('equal',   0,2, 0,2),
 
168
                                     ('replace', 2,3, 2,5),
 
169
                                     ('equal',   3,5, 5,7)
 
170
                                    ])
 
171
        chk_ops('abde', 'abXYZde', [('equal',  0,2, 0,2),
 
172
                                    ('insert', 2,2, 2,5),
 
173
                                    ('equal',  2,4, 5,7)
 
174
                                   ])
 
175
        chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
 
176
                [('equal',  0,6,  0,6),
 
177
                 ('insert', 6,6,  6,11),
 
178
                 ('equal',  6,16, 11,21)
 
179
                ])
 
180
        chk_ops(
 
181
                [ 'hello there\n'
 
182
                , 'world\n'
 
183
                , 'how are you today?\n'],
 
184
                [ 'hello there\n'
 
185
                , 'how are you today?\n'],
 
186
                [('equal',  0,1, 0,1),
 
187
                 ('delete', 1,2, 1,1),
 
188
                 ('equal',  2,3, 1,2)
 
189
                ])
 
190
        chk_ops('aBccDe', 'abccde', 
 
191
                [('equal',   0,1, 0,1),
 
192
                 ('replace', 1,2, 1,2),
 
193
                 ('equal',   2,4, 2,4),
 
194
                 ('replace', 4,5, 4,5),
 
195
                 ('equal',   5,6, 5,6)
 
196
                ])
 
197
        chk_ops('aBcdEcdFg', 'abcdecdfg', 
 
198
                [('equal',   0,1, 0,1),
 
199
                 ('replace', 1,2, 1,2),
 
200
                 ('equal',   2,4, 2,4),
 
201
                 ('replace', 4,5, 4,5),
 
202
                 ('equal',   5,7, 5,7),
 
203
                 ('replace', 7,8, 7,8),
 
204
                 ('equal',   8,9, 8,9)
 
205
                ])
 
206
 
 
207
    def test_multiple_ranges(self):
 
208
        # There was an earlier bug where we used a bad set of ranges,
 
209
        # this triggers that specific bug, to make sure it doesn't regress
 
210
        def chk_blocks(a, b, matching):
 
211
            # difflib always adds a signature of the total
 
212
            # length, with no matching entries at the end
 
213
            s = SequenceMatcher(None, a, b)
 
214
            blocks = s.get_matching_blocks()
 
215
            x = blocks.pop()
 
216
            self.assertEquals(x, (len(a), len(b), 0))
 
217
            self.assertEquals(matching, blocks)
 
218
 
 
219
        chk_blocks('abcdefghijklmnop'
 
220
                 , 'abcXghiYZQRSTUVWXYZijklmnop'
 
221
                 , [(0, 0, 3), (6, 4, 3), (9, 20, 7)])
 
222
 
 
223
        chk_blocks('ABCd efghIjk  L'
 
224
                 , 'AxyzBCn mo pqrstuvwI1 2  L'
 
225
                 , [(0,0,1), (1, 4, 2), (4, 7, 1), (9, 19, 1), (12, 23, 3)])
 
226
 
 
227
        chk_blocks('''\
 
228
    get added when you add a file in the directory.
 
229
    """
 
230
    takes_args = ['file*']
 
231
    takes_options = ['no-recurse']
 
232
    
 
233
    def run(self, file_list, no_recurse=False):
 
234
        from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
 
235
        if is_quiet():
 
236
            reporter = add_reporter_null
 
237
        else:
 
238
            reporter = add_reporter_print
 
239
        smart_add(file_list, not no_recurse, reporter)
 
240
 
 
241
 
 
242
class cmd_mkdir(Command):
 
243
'''.splitlines(True)
 
244
, '''\
 
245
    get added when you add a file in the directory.
 
246
 
 
247
    --dry-run will show which files would be added, but not actually 
 
248
    add them.
 
249
    """
 
250
    takes_args = ['file*']
 
251
    takes_options = ['no-recurse', 'dry-run']
 
252
 
 
253
    def run(self, file_list, no_recurse=False, dry_run=False):
 
254
        import bzrlib.add
 
255
 
 
256
        if dry_run:
 
257
            if is_quiet():
 
258
                # This is pointless, but I'd rather not raise an error
 
259
                action = bzrlib.add.add_action_null
 
260
            else:
 
261
                action = bzrlib.add.add_action_print
 
262
        elif is_quiet():
 
263
            action = bzrlib.add.add_action_add
 
264
        else:
 
265
            action = bzrlib.add.add_action_add_and_print
 
266
 
 
267
        bzrlib.add.smart_add(file_list, not no_recurse, action)
 
268
 
 
269
 
 
270
class cmd_mkdir(Command):
 
271
'''.splitlines(True)
 
272
, [(0,0,1), (1, 4, 2), (9, 19, 1), (12, 23, 3)])
 
273
 
 
274
    def test_cdv_unified_diff(self):
 
275
        txt_a = ['hello there\n',
 
276
                 'world\n',
 
277
                 'how are you today?\n']
 
278
        txt_b = ['hello there\n',
 
279
                 'how are you today?\n']
 
280
        self.assertEquals([ '---  \n',
 
281
                           '+++  \n',
 
282
                           '@@ -1,3 +1,2 @@\n',
 
283
                           ' hello there\n',
 
284
                           '-world\n',
 
285
                           ' how are you today?\n'
 
286
                          ]
 
287
                          , list(unified_diff(txt_a, txt_b
 
288
                                        , sequencematcher=SequenceMatcher)))
 
289
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
 
290
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
 
291
        # This is the result with LongestCommonSubstring matching
 
292
        self.assertEquals(['---  \n',
 
293
                           '+++  \n',
 
294
                           '@@ -1,6 +1,11 @@\n',
 
295
                           ' a\n',
 
296
                           ' b\n',
 
297
                           ' c\n',
 
298
                           '+d\n',
 
299
                           '+e\n',
 
300
                           '+f\n',
 
301
                           '+x\n',
 
302
                           '+y\n',
 
303
                           ' d\n',
 
304
                           ' e\n',
 
305
                           ' f\n']
 
306
                          , list(unified_diff(txt_a, txt_b)))
 
307
        # And the cdv diff
 
308
        self.assertEquals(['---  \n',
 
309
                           '+++  \n',
 
310
                           '@@ -4,6 +4,11 @@\n',
 
311
                           ' d\n',
 
312
                           ' e\n',
 
313
                           ' f\n',
 
314
                           '+x\n',
 
315
                           '+y\n',
 
316
                           '+d\n',
 
317
                           '+e\n',
 
318
                           '+f\n',
 
319
                           ' g\n',
 
320
                           ' h\n',
 
321
                           ' i\n',
 
322
                          ]
 
323
                          , list(unified_diff(txt_a, txt_b,
 
324
                                 sequencematcher=SequenceMatcher)))
 
325
 
 
326
 
 
327
class TestCDVDiffLibFiles(TestCaseInTempDir):
 
328
 
 
329
    def test_cdv_unified_diff_files(self):
 
330
        txt_a = ['hello there\n',
 
331
                 'world\n',
 
332
                 'how are you today?\n']
 
333
        txt_b = ['hello there\n',
 
334
                 'how are you today?\n']
 
335
        open('a1', 'wb').writelines(txt_a)
 
336
        open('b1', 'wb').writelines(txt_b)
 
337
 
 
338
        self.assertEquals(['--- a1 \n',
 
339
                           '+++ b1 \n',
 
340
                           '@@ -1,3 +1,2 @@\n',
 
341
                           ' hello there\n',
 
342
                           '-world\n',
 
343
                           ' how are you today?\n',
 
344
                          ]
 
345
                          , list(unified_diff_files('a1', 'b1',
 
346
                                 sequencematcher=SequenceMatcher)))
 
347
 
 
348
        txt_a = map(lambda x: x+'\n', 'abcdefghijklmnop')
 
349
        txt_b = map(lambda x: x+'\n', 'abcdefxydefghijklmnop')
 
350
        open('a2', 'wb').writelines(txt_a)
 
351
        open('b2', 'wb').writelines(txt_b)
 
352
 
 
353
        # This is the result with LongestCommonSubstring matching
 
354
        self.assertEquals(['--- a2 \n',
 
355
                           '+++ b2 \n',
 
356
                           '@@ -1,6 +1,11 @@\n',
 
357
                           ' a\n',
 
358
                           ' b\n',
 
359
                           ' c\n',
 
360
                           '+d\n',
 
361
                           '+e\n',
 
362
                           '+f\n',
 
363
                           '+x\n',
 
364
                           '+y\n',
 
365
                           ' d\n',
 
366
                           ' e\n',
 
367
                           ' f\n']
 
368
                          , list(unified_diff_files('a2', 'b2')))
 
369
 
 
370
        # And the cdv diff
 
371
        self.assertEquals(['--- a2 \n',
 
372
                           '+++ b2 \n',
 
373
                           '@@ -4,6 +4,11 @@\n',
 
374
                           ' d\n',
 
375
                           ' e\n',
 
376
                           ' f\n',
 
377
                           '+x\n',
 
378
                           '+y\n',
 
379
                           '+d\n',
 
380
                           '+e\n',
 
381
                           '+f\n',
 
382
                           ' g\n',
 
383
                           ' h\n',
 
384
                           ' i\n',
 
385
                          ]
 
386
                          , list(unified_diff_files('a2', 'b2',
 
387
                                 sequencematcher=SequenceMatcher)))