~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_patches.py

  • Committer: John Arbash Meinel
  • Date: 2009-10-30 14:07:31 UTC
  • mto: (4634.93.1 2.0.2)
  • mto: This revision was merged to the branch mainline in revision 4782.
  • Revision ID: john@arbash-meinel.com-20091030140731-sjv0pr90ffvuqjst
Update the download location registered with pypi.

Show diffs side-by-side

added added

removed removed

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