/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 breezy/tests/test_patches.py

  • Committer: Martin
  • Date: 2018-11-16 19:10:17 UTC
  • mto: This revision was merged to the branch mainline in revision 7177.
  • Revision ID: gzlist@googlemail.com-20181116191017-kyedz1qck0ovon3h
Remove lazy_regexp reset in bt.test_source

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2010 Aaron Bentley, Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
 
 
18
import os.path
 
19
 
 
20
from breezy.tests import TestCase
 
21
 
 
22
from breezy.iterablefile import IterableFile
 
23
from breezy.patches import (MalformedLine,
 
24
                            MalformedHunkHeader,
 
25
                            MalformedPatchHeader,
 
26
                            BinaryPatch,
 
27
                            BinaryFiles,
 
28
                            Patch,
 
29
                            ContextLine,
 
30
                            InsertLine,
 
31
                            RemoveLine,
 
32
                            difference_index,
 
33
                            get_patch_names,
 
34
                            hunk_from_header,
 
35
                            iter_patched,
 
36
                            iter_patched_from_hunks,
 
37
                            parse_line,
 
38
                            parse_patch,
 
39
                            parse_patches,
 
40
                            NO_NL)
 
41
 
 
42
 
 
43
class PatchesTester(TestCase):
 
44
 
 
45
    def datafile(self, filename):
 
46
        data_path = os.path.join(os.path.dirname(__file__),
 
47
                                 "test_patches_data", filename)
 
48
        return open(data_path, "rb")
 
49
 
 
50
    def data_lines(self, filename):
 
51
        with self.datafile(filename) as datafile:
 
52
            return datafile.readlines()
 
53
 
 
54
    def test_parse_patches_leading_noise(self):
 
55
        # https://bugs.launchpad.net/bzr/+bug/502076
 
56
        # https://code.launchpad.net/~toshio/bzr/allow-dirty-patches/+merge/18854
 
57
        lines = [b"diff -pruN commands.py",
 
58
                 b"--- orig/commands.py",
 
59
                 b"+++ mod/dommands.py"]
 
60
        bits = list(parse_patches(iter(lines), allow_dirty=True))
 
61
 
 
62
    def test_preserve_dirty_head(self):
 
63
        """Parse a patch containing a dirty header, and preserve lines"""
 
64
        lines = [b"=== added directory 'foo/bar'\n",
 
65
                 b"=== modified file 'orig/commands.py'\n",
 
66
                 b"--- orig/commands.py\n",
 
67
                 b"+++ mod/dommands.py\n",
 
68
                 b"=== modified file 'orig/another.py'\n",
 
69
                 b"--- orig/another.py\n",
 
70
                 b"+++ mod/another.py\n"]
 
71
        patches = list(parse_patches(
 
72
            lines.__iter__(), allow_dirty=True, keep_dirty=True))
 
73
        self.assertLength(2, patches)
 
74
        self.assertEqual(patches[0]['dirty_head'],
 
75
                         [b"=== added directory 'foo/bar'\n",
 
76
                          b"=== modified file 'orig/commands.py'\n"])
 
77
        self.assertEqual(patches[0]['patch'].get_header().splitlines(True),
 
78
                         [b"--- orig/commands.py\n", b"+++ mod/dommands.py\n"])
 
79
        self.assertEqual(patches[1]['dirty_head'],
 
80
                         [b"=== modified file 'orig/another.py'\n"])
 
81
        self.assertEqual(patches[1]['patch'].get_header().splitlines(True),
 
82
                         [b"--- orig/another.py\n", b"+++ mod/another.py\n"])
 
83
 
 
84
    def testValidPatchHeader(self):
 
85
        """Parse a valid patch header"""
 
86
        lines = b"--- orig/commands.py\n+++ mod/dommands.py\n".split(b'\n')
 
87
        (orig, mod) = get_patch_names(lines.__iter__())
 
88
        self.assertEqual(orig, b"orig/commands.py")
 
89
        self.assertEqual(mod, b"mod/dommands.py")
 
90
 
 
91
    def testInvalidPatchHeader(self):
 
92
        """Parse an invalid patch header"""
 
93
        lines = b"-- orig/commands.py\n+++ mod/dommands.py".split(b'\n')
 
94
        self.assertRaises(MalformedPatchHeader, get_patch_names,
 
95
                          lines.__iter__())
 
96
 
 
97
    def testValidHunkHeader(self):
 
98
        """Parse a valid hunk header"""
 
99
        header = b"@@ -34,11 +50,6 @@\n"
 
100
        hunk = hunk_from_header(header)
 
101
        self.assertEqual(hunk.orig_pos, 34)
 
102
        self.assertEqual(hunk.orig_range, 11)
 
103
        self.assertEqual(hunk.mod_pos, 50)
 
104
        self.assertEqual(hunk.mod_range, 6)
 
105
        self.assertEqual(hunk.as_bytes(), header)
 
106
 
 
107
    def testValidHunkHeader2(self):
 
108
        """Parse a tricky, valid hunk header"""
 
109
        header = b"@@ -1 +0,0 @@\n"
 
110
        hunk = hunk_from_header(header)
 
111
        self.assertEqual(hunk.orig_pos, 1)
 
112
        self.assertEqual(hunk.orig_range, 1)
 
113
        self.assertEqual(hunk.mod_pos, 0)
 
114
        self.assertEqual(hunk.mod_range, 0)
 
115
        self.assertEqual(hunk.as_bytes(), header)
 
116
 
 
117
    def testPDiff(self):
 
118
        """Parse a hunk header produced by diff -p"""
 
119
        header = b"@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
 
120
        hunk = hunk_from_header(header)
 
121
        self.assertEqual(b'bzr 0.18rc1  2007-07-10', hunk.tail)
 
122
        self.assertEqual(header, hunk.as_bytes())
 
123
 
 
124
    def makeMalformed(self, header):
 
125
        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
 
126
 
 
127
    def testInvalidHeader(self):
 
128
        """Parse an invalid hunk header"""
 
129
        self.makeMalformed(b" -34,11 +50,6 \n")
 
130
        self.makeMalformed(b"@@ +50,6 -34,11 @@\n")
 
131
        self.makeMalformed(b"@@ -34,11 +50,6 @@")
 
132
        self.makeMalformed(b"@@ -34.5,11 +50,6 @@\n")
 
133
        self.makeMalformed(b"@@-34,11 +50,6@@\n")
 
134
        self.makeMalformed(b"@@ 34,11 50,6 @@\n")
 
135
        self.makeMalformed(b"@@ -34,11 @@\n")
 
136
        self.makeMalformed(b"@@ -34,11 +50,6.5 @@\n")
 
137
        self.makeMalformed(b"@@ -34,11 +50,-6 @@\n")
 
138
 
 
139
    def lineThing(self, text, type):
 
140
        line = parse_line(text)
 
141
        self.assertIsInstance(line, type)
 
142
        self.assertEqual(line.as_bytes(), text)
 
143
 
 
144
    def makeMalformedLine(self, text):
 
145
        self.assertRaises(MalformedLine, parse_line, text)
 
146
 
 
147
    def testValidLine(self):
 
148
        """Parse a valid hunk line"""
 
149
        self.lineThing(b" hello\n", ContextLine)
 
150
        self.lineThing(b"+hello\n", InsertLine)
 
151
        self.lineThing(b"-hello\n", RemoveLine)
 
152
 
 
153
    def testMalformedLine(self):
 
154
        """Parse invalid valid hunk lines"""
 
155
        self.makeMalformedLine(b"hello\n")
 
156
 
 
157
    def testMalformedLineNO_NL(self):
 
158
        """Parse invalid '\\ No newline at end of file' in hunk lines"""
 
159
        self.makeMalformedLine(NO_NL)
 
160
 
 
161
    def compare_parsed(self, patchtext):
 
162
        lines = patchtext.splitlines(True)
 
163
        patch = parse_patch(lines.__iter__())
 
164
        pstr = patch.as_bytes()
 
165
        i = difference_index(patchtext, pstr)
 
166
        if i is not None:
 
167
            print("%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i]))
 
168
        self.assertEqual(patchtext, patch.as_bytes())
 
169
 
 
170
    def testAll(self):
 
171
        """Test parsing a whole patch"""
 
172
        with self.datafile("patchtext.patch") as f:
 
173
            patchtext = f.read()
 
174
        self.compare_parsed(patchtext)
 
175
 
 
176
    def test_parse_binary(self):
 
177
        """Test parsing a whole patch"""
 
178
        patches = list(parse_patches(self.data_lines("binary.patch")))
 
179
        self.assertIs(BinaryPatch, patches[0].__class__)
 
180
        self.assertIs(Patch, patches[1].__class__)
 
181
        self.assertContainsRe(patches[0].oldname, b'^bar\t')
 
182
        self.assertContainsRe(patches[0].newname, b'^qux\t')
 
183
        self.assertContainsRe(patches[0].as_bytes(),
 
184
                              b'Binary files bar\t.* and qux\t.* differ\n')
 
185
 
 
186
    def test_parse_binary_after_normal(self):
 
187
        patches = list(parse_patches(
 
188
            self.data_lines("binary-after-normal.patch")))
 
189
        self.assertIs(BinaryPatch, patches[1].__class__)
 
190
        self.assertIs(Patch, patches[0].__class__)
 
191
        self.assertContainsRe(patches[1].oldname, b'^bar\t')
 
192
        self.assertContainsRe(patches[1].newname, b'^qux\t')
 
193
        self.assertContainsRe(patches[1].as_bytes(),
 
194
                              b'Binary files bar\t.* and qux\t.* differ\n')
 
195
 
 
196
    def test_roundtrip_binary(self):
 
197
        patchtext = b''.join(self.data_lines("binary.patch"))
 
198
        patches = parse_patches(patchtext.splitlines(True))
 
199
        self.assertEqual(patchtext, b''.join(p.as_bytes() for p in patches))
 
200
 
 
201
    def testInit(self):
 
202
        """Handle patches missing half the position, range tuple"""
 
203
        patchtext = \
 
204
            b"""--- orig/__vavg__.cl
 
205
+++ mod/__vavg__.cl
 
206
@@ -1 +1,2 @@
 
207
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
 
208
+__qbp__ = Na nygreangr Nepu pbzznaqyvar vagresnpr
 
209
"""
 
210
        self.compare_parsed(patchtext)
 
211
 
 
212
    def testLineLookup(self):
 
213
        """Make sure we can accurately look up mod line from orig"""
 
214
        patch = parse_patch(self.datafile("diff"))
 
215
        orig = list(self.datafile("orig"))
 
216
        mod = list(self.datafile("mod"))
 
217
        removals = []
 
218
        for i in range(len(orig)):
 
219
            mod_pos = patch.pos_in_mod(i)
 
220
            if mod_pos is None:
 
221
                removals.append(orig[i])
 
222
                continue
 
223
            self.assertEqual(mod[mod_pos], orig[i])
 
224
        rem_iter = removals.__iter__()
 
225
        for hunk in patch.hunks:
 
226
            for line in hunk.lines:
 
227
                if isinstance(line, RemoveLine):
 
228
                    self.assertEqual(line.contents, next(rem_iter))
 
229
        self.assertRaises(StopIteration, next, rem_iter)
 
230
 
 
231
    def testPatching(self):
 
232
        """Test a few patch files, and make sure they work."""
 
233
        files = [
 
234
            ('diff-2', 'orig-2', 'mod-2'),
 
235
            ('diff-3', 'orig-3', 'mod-3'),
 
236
            ('diff-4', 'orig-4', 'mod-4'),
 
237
            ('diff-5', 'orig-5', 'mod-5'),
 
238
            ('diff-6', 'orig-6', 'mod-6'),
 
239
            ('diff-7', 'orig-7', 'mod-7'),
 
240
        ]
 
241
        for diff, orig, mod in files:
 
242
            patch = self.datafile(diff)
 
243
            orig_lines = list(self.datafile(orig))
 
244
            mod_lines = list(self.datafile(mod))
 
245
 
 
246
            patched_file = IterableFile(iter_patched(orig_lines, patch))
 
247
            count = 0
 
248
            for patch_line in patched_file:
 
249
                self.assertEqual(patch_line, mod_lines[count])
 
250
                count += 1
 
251
            self.assertEqual(count, len(mod_lines))
 
252
 
 
253
    def test_iter_patched_binary(self):
 
254
        binary_lines = self.data_lines('binary.patch')
 
255
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
 
256
 
 
257
    def test_iter_patched_from_hunks(self):
 
258
        """Test a few patch files, and make sure they work."""
 
259
        files = [
 
260
            ('diff-2', 'orig-2', 'mod-2'),
 
261
            ('diff-3', 'orig-3', 'mod-3'),
 
262
            ('diff-4', 'orig-4', 'mod-4'),
 
263
            ('diff-5', 'orig-5', 'mod-5'),
 
264
            ('diff-6', 'orig-6', 'mod-6'),
 
265
            ('diff-7', 'orig-7', 'mod-7'),
 
266
        ]
 
267
        for diff, orig, mod in files:
 
268
            parsed = parse_patch(self.datafile(diff))
 
269
            orig_lines = list(self.datafile(orig))
 
270
            mod_lines = list(self.datafile(mod))
 
271
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
 
272
            patched_file = IterableFile(iter_patched)
 
273
            count = 0
 
274
            for patch_line in patched_file:
 
275
                self.assertEqual(patch_line, mod_lines[count])
 
276
                count += 1
 
277
            self.assertEqual(count, len(mod_lines))
 
278
 
 
279
    def testFirstLineRenumber(self):
 
280
        """Make sure we handle lines at the beginning of the hunk"""
 
281
        patch = parse_patch(self.datafile("insert_top.patch"))
 
282
        self.assertEqual(patch.pos_in_mod(0), 1)
 
283
 
 
284
    def testParsePatches(self):
 
285
        """Make sure file names can be extracted from tricky unified diffs"""
 
286
        patchtext = \
 
287
            b"""--- orig-7
 
288
+++ mod-7
 
289
@@ -1,10 +1,10 @@
 
290
 -- a
 
291
--- b
 
292
+++ c
 
293
 xx d
 
294
 xx e
 
295
 ++ f
 
296
-++ g
 
297
+-- h
 
298
 xx i
 
299
 xx j
 
300
 -- k
 
301
--- l
 
302
+++ m
 
303
--- orig-8
 
304
+++ mod-8
 
305
@@ -1 +1 @@
 
306
--- A
 
307
+++ B
 
308
@@ -1 +1 @@
 
309
--- C
 
310
+++ D
 
311
"""
 
312
        filenames = [(b'orig-7', b'mod-7'),
 
313
                     (b'orig-8', b'mod-8')]
 
314
        patches = parse_patches(patchtext.splitlines(True))
 
315
        patch_files = []
 
316
        for patch in patches:
 
317
            patch_files.append((patch.oldname, patch.newname))
 
318
        self.assertEqual(patch_files, filenames)
 
319
 
 
320
    def testStatsValues(self):
 
321
        """Test the added, removed and hunks values for stats_values."""
 
322
        patch = parse_patch(self.datafile("diff"))
 
323
        self.assertEqual((299, 407, 48), patch.stats_values())