~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_patches.py

  • Committer: Vincent Ladeuil
  • Date: 2009-07-10 08:33:11 UTC
  • mfrom: (4503.1.3 tree-has-changes)
  • mto: This revision was merged to the branch mainline in revision 4525.
  • Revision ID: v.ladeuil+lp@free.fr-20090710083311-ulnr2ic6lvevjr3a
Quicker check for changes in mutable trees

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
                            ContextLine,
 
28
                            InsertLine,
 
29
                            RemoveLine,
 
30
                            difference_index,
 
31
                            get_patch_names,
 
32
                            hunk_from_header,
 
33
                            iter_patched,
 
34
                            iter_patched_from_hunks,
 
35
                            parse_line,
 
36
                            parse_patch,
 
37
                            parse_patches,
 
38
                            NO_NL)
 
39
 
 
40
 
 
41
class PatchesTester(TestCase):
 
42
 
 
43
    def datafile(self, filename):
 
44
        data_path = os.path.join(os.path.dirname(__file__),
 
45
                                 "test_patches_data", filename)
 
46
        return file(data_path, "rb")
 
47
 
 
48
    def testValidPatchHeader(self):
 
49
        """Parse a valid patch header"""
 
50
        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
 
51
        (orig, mod) = get_patch_names(lines.__iter__())
 
52
        self.assertEqual(orig, "orig/commands.py")
 
53
        self.assertEqual(mod, "mod/dommands.py")
 
54
 
 
55
    def testInvalidPatchHeader(self):
 
56
        """Parse an invalid patch header"""
 
57
        lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
 
58
        self.assertRaises(MalformedPatchHeader, get_patch_names,
 
59
                          lines.__iter__())
 
60
 
 
61
    def testValidHunkHeader(self):
 
62
        """Parse a valid hunk header"""
 
63
        header = "@@ -34,11 +50,6 @@\n"
 
64
        hunk = hunk_from_header(header);
 
65
        self.assertEqual(hunk.orig_pos, 34)
 
66
        self.assertEqual(hunk.orig_range, 11)
 
67
        self.assertEqual(hunk.mod_pos, 50)
 
68
        self.assertEqual(hunk.mod_range, 6)
 
69
        self.assertEqual(str(hunk), header)
 
70
 
 
71
    def testValidHunkHeader2(self):
 
72
        """Parse a tricky, valid hunk header"""
 
73
        header = "@@ -1 +0,0 @@\n"
 
74
        hunk = hunk_from_header(header);
 
75
        self.assertEqual(hunk.orig_pos, 1)
 
76
        self.assertEqual(hunk.orig_range, 1)
 
77
        self.assertEqual(hunk.mod_pos, 0)
 
78
        self.assertEqual(hunk.mod_range, 0)
 
79
        self.assertEqual(str(hunk), header)
 
80
 
 
81
    def testPDiff(self):
 
82
        """Parse a hunk header produced by diff -p"""
 
83
        header = "@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
 
84
        hunk = hunk_from_header(header)
 
85
        self.assertEqual('bzr 0.18rc1  2007-07-10', hunk.tail)
 
86
        self.assertEqual(header, str(hunk))
 
87
 
 
88
    def makeMalformed(self, header):
 
89
        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
 
90
 
 
91
    def testInvalidHeader(self):
 
92
        """Parse an invalid hunk header"""
 
93
        self.makeMalformed(" -34,11 +50,6 \n")
 
94
        self.makeMalformed("@@ +50,6 -34,11 @@\n")
 
95
        self.makeMalformed("@@ -34,11 +50,6 @@")
 
96
        self.makeMalformed("@@ -34.5,11 +50,6 @@\n")
 
97
        self.makeMalformed("@@-34,11 +50,6@@\n")
 
98
        self.makeMalformed("@@ 34,11 50,6 @@\n")
 
99
        self.makeMalformed("@@ -34,11 @@\n")
 
100
        self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
 
101
        self.makeMalformed("@@ -34,11 +50,-6 @@\n")
 
102
 
 
103
    def lineThing(self,text, type):
 
104
        line = parse_line(text)
 
105
        self.assertIsInstance(line, type)
 
106
        self.assertEqual(str(line), text)
 
107
 
 
108
    def makeMalformedLine(self, text):
 
109
        self.assertRaises(MalformedLine, parse_line, text)
 
110
 
 
111
    def testValidLine(self):
 
112
        """Parse a valid hunk line"""
 
113
        self.lineThing(" hello\n", ContextLine)
 
114
        self.lineThing("+hello\n", InsertLine)
 
115
        self.lineThing("-hello\n", RemoveLine)
 
116
 
 
117
    def testMalformedLine(self):
 
118
        """Parse invalid valid hunk lines"""
 
119
        self.makeMalformedLine("hello\n")
 
120
 
 
121
    def testMalformedLineNO_NL(self):
 
122
        """Parse invalid '\ No newline at end of file' in hunk lines"""
 
123
        self.makeMalformedLine(NO_NL)
 
124
 
 
125
    def compare_parsed(self, patchtext):
 
126
        lines = patchtext.splitlines(True)
 
127
        patch = parse_patch(lines.__iter__())
 
128
        pstr = str(patch)
 
129
        i = difference_index(patchtext, pstr)
 
130
        if i is not None:
 
131
            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
 
132
        self.assertEqual (patchtext, str(patch))
 
133
 
 
134
    def testAll(self):
 
135
        """Test parsing a whole patch"""
 
136
        patchtext = self.datafile("patchtext.patch").read()
 
137
        self.compare_parsed(patchtext)
 
138
 
 
139
    def testInit(self):
 
140
        """Handle patches missing half the position, range tuple"""
 
141
        patchtext = \
 
142
"""--- orig/__vavg__.cl
 
143
+++ mod/__vavg__.cl
 
144
@@ -1 +1,2 @@
 
145
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
 
146
+__qbp__ = Na nygreangr Nepu pbzznaqyvar vagresnpr
 
147
"""
 
148
        self.compare_parsed(patchtext)
 
149
 
 
150
    def testLineLookup(self):
 
151
        import sys
 
152
        """Make sure we can accurately look up mod line from orig"""
 
153
        patch = parse_patch(self.datafile("diff"))
 
154
        orig = list(self.datafile("orig"))
 
155
        mod = list(self.datafile("mod"))
 
156
        removals = []
 
157
        for i in range(len(orig)):
 
158
            mod_pos = patch.pos_in_mod(i)
 
159
            if mod_pos is None:
 
160
                removals.append(orig[i])
 
161
                continue
 
162
            self.assertEqual(mod[mod_pos], orig[i])
 
163
        rem_iter = removals.__iter__()
 
164
        for hunk in patch.hunks:
 
165
            for line in hunk.lines:
 
166
                if isinstance(line, RemoveLine):
 
167
                    next = rem_iter.next()
 
168
                    if line.contents != next:
 
169
                        sys.stdout.write(" orig:%spatch:%s" % (next,
 
170
                                         line.contents))
 
171
                    self.assertEqual(line.contents, next)
 
172
        self.assertRaises(StopIteration, rem_iter.next)
 
173
 
 
174
    def testPatching(self):
 
175
        """Test a few patch files, and make sure they work."""
 
176
        files = [
 
177
            ('diff-2', 'orig-2', 'mod-2'),
 
178
            ('diff-3', 'orig-3', 'mod-3'),
 
179
            ('diff-4', 'orig-4', 'mod-4'),
 
180
            ('diff-5', 'orig-5', 'mod-5'),
 
181
            ('diff-6', 'orig-6', 'mod-6'),
 
182
            ('diff-7', 'orig-7', 'mod-7'),
 
183
        ]
 
184
        for diff, orig, mod in files:
 
185
            patch = self.datafile(diff)
 
186
            orig_lines = list(self.datafile(orig))
 
187
            mod_lines = list(self.datafile(mod))
 
188
 
 
189
            patched_file = IterableFile(iter_patched(orig_lines, patch))
 
190
            lines = []
 
191
            count = 0
 
192
            for patch_line in patched_file:
 
193
                self.assertEqual(patch_line, mod_lines[count])
 
194
                count += 1
 
195
            self.assertEqual(count, len(mod_lines))
 
196
 
 
197
    def test_iter_patched_from_hunks(self):
 
198
        """Test a few patch files, and make sure they work."""
 
199
        files = [
 
200
            ('diff-2', 'orig-2', 'mod-2'),
 
201
            ('diff-3', 'orig-3', 'mod-3'),
 
202
            ('diff-4', 'orig-4', 'mod-4'),
 
203
            ('diff-5', 'orig-5', 'mod-5'),
 
204
            ('diff-6', 'orig-6', 'mod-6'),
 
205
            ('diff-7', 'orig-7', 'mod-7'),
 
206
        ]
 
207
        for diff, orig, mod in files:
 
208
            parsed = parse_patch(self.datafile(diff))
 
209
            orig_lines = list(self.datafile(orig))
 
210
            mod_lines = list(self.datafile(mod))
 
211
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
 
212
            patched_file = IterableFile(iter_patched)
 
213
            lines = []
 
214
            count = 0
 
215
            for patch_line in patched_file:
 
216
                self.assertEqual(patch_line, mod_lines[count])
 
217
                count += 1
 
218
            self.assertEqual(count, len(mod_lines))
 
219
 
 
220
    def testFirstLineRenumber(self):
 
221
        """Make sure we handle lines at the beginning of the hunk"""
 
222
        patch = parse_patch(self.datafile("insert_top.patch"))
 
223
        self.assertEqual(patch.pos_in_mod(0), 1)
 
224
 
 
225
    def testParsePatches(self):
 
226
        """Make sure file names can be extracted from tricky unified diffs"""
 
227
        patchtext = \
 
228
"""--- orig-7
 
229
+++ mod-7
 
230
@@ -1,10 +1,10 @@
 
231
 -- a
 
232
--- b
 
233
+++ c
 
234
 xx d
 
235
 xx e
 
236
 ++ f
 
237
-++ g
 
238
+-- h
 
239
 xx i
 
240
 xx j
 
241
 -- k
 
242
--- l
 
243
+++ m
 
244
--- orig-8
 
245
+++ mod-8
 
246
@@ -1 +1 @@
 
247
--- A
 
248
+++ B
 
249
@@ -1 +1 @@
 
250
--- C
 
251
+++ D
 
252
"""
 
253
        filenames = [('orig-7', 'mod-7'),
 
254
                     ('orig-8', 'mod-8')]
 
255
        patches = parse_patches(patchtext.splitlines(True))
 
256
        patch_files = []
 
257
        for patch in patches:
 
258
            patch_files.append((patch.oldname, patch.newname))
 
259
        self.assertEqual(patch_files, filenames)
 
260
 
 
261
    def testStatsValues(self):
 
262
        """Test the added, removed and hunks values for stats_values."""
 
263
        patch = parse_patch(self.datafile("diff"))
 
264
        self.assertEqual((299, 407, 48), patch.stats_values())