/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: Jelmer Vernooij
  • Date: 2018-05-19 13:16:11 UTC
  • mto: (6968.4.3 git-archive)
  • mto: This revision was merged to the branch mainline in revision 6972.
  • Revision ID: jelmer@jelmer.uk-20180519131611-l9h9ud41j7qg1m03
Move tar/zip to breezy.archive.

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