~bzr-pqm/bzr/bzr.dev

3376.2.6 by Martin Pool
Make PatchesTester use bzrlib TestCase base
1
# Copyright (C) 2004 - 2008 Aaron Bentley, Canonical Ltd
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
2
# <aaron.bentley@utoronto.ca>
3
#
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
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
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.82.127 by Aaron Bentley
PEP8 updates
17
18
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
19
import os.path
20
3376.2.6 by Martin Pool
Make PatchesTester use bzrlib TestCase base
21
from bzrlib.tests import TestCase
22
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
23
from bzrlib.iterablefile import IterableFile
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
24
from bzrlib.patches import (MalformedLine,
25
                            MalformedHunkHeader,
26
                            MalformedPatchHeader,
4634.80.1 by Aaron Bentley
Parse binary files.
27
                            BinaryPatch,
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
28
                            BinaryFiles,
4634.80.1 by Aaron Bentley
Parse binary files.
29
                            Patch,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
30
                            ContextLine,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
31
                            InsertLine,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
32
                            RemoveLine,
33
                            difference_index,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
34
                            get_patch_names,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
35
                            hunk_from_header,
36
                            iter_patched,
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
37
                            iter_patched_from_hunks,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
38
                            parse_line,
2298.6.1 by Johan Dahlberg
Fix bzrtools shelve command for removed lines beginning with "--"
39
                            parse_patch,
3873.1.2 by Benoît Pierre
Add patches test for 'No newline at end of file' in the middle of hunk lines.
40
                            parse_patches,
41
                            NO_NL)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
42
1185.82.127 by Aaron Bentley
PEP8 updates
43
3376.2.6 by Martin Pool
Make PatchesTester use bzrlib TestCase base
44
class PatchesTester(TestCase):
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
45
1185.82.7 by John Arbash Meinel
Adding patches.py into bzrlib, including the tests into the test suite.
46
    def datafile(self, filename):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
47
        data_path = os.path.join(os.path.dirname(__file__),
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
48
                                 "test_patches_data", filename)
1185.82.7 by John Arbash Meinel
Adding patches.py into bzrlib, including the tests into the test suite.
49
        return file(data_path, "rb")
50
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
51
    def data_lines(self, filename):
52
        datafile = self.datafile(filename)
53
        try:
54
            return datafile.readlines()
55
        finally:
56
            datafile.close()
57
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
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__())
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
62
        self.assertEqual(orig, "orig/commands.py")
63
        self.assertEqual(mod, "mod/dommands.py")
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
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);
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
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)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
80
81
    def testValidHunkHeader2(self):
82
        """Parse a tricky, valid hunk header"""
83
        header = "@@ -1 +0,0 @@\n"
84
        hunk = hunk_from_header(header);
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
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)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
90
1551.18.6 by Aaron Bentley
Add support for diff -p-style diffs to patch parser
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
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
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)
3376.2.5 by Martin Pool
Remove a few more elusive assert statements
115
        self.assertIsInstance(line, type)
116
        self.assertEqual(str(line), text)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
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)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
126
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
127
    def testMalformedLine(self):
128
        """Parse invalid valid hunk lines"""
129
        self.makeMalformedLine("hello\n")
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
130
3873.1.2 by Benoît Pierre
Add patches test for 'No newline at end of file' in the middle of hunk lines.
131
    def testMalformedLineNO_NL(self):
3873.1.4 by Benoît Pierre
Add another test for patches to check we correctly handle '\ No newline
132
        """Parse invalid '\ No newline at end of file' in hunk lines"""
3873.1.2 by Benoît Pierre
Add patches test for 'No newline at end of file' in the middle of hunk lines.
133
        self.makeMalformedLine(NO_NL)
134
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
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"""
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
146
        patchtext = self.datafile("patchtext.patch").read()
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
147
        self.compare_parsed(patchtext)
148
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
149
    def test_parse_binary(self):
4634.80.1 by Aaron Bentley
Parse binary files.
150
        """Test parsing a whole patch"""
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
151
        patches = parse_patches(self.data_lines("binary.patch"))
4634.80.1 by Aaron Bentley
Parse binary files.
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]),
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
157
                                  'Binary files bar\t.* and qux\t.* differ\n')
158
4634.98.1 by Aaron Bentley
Improve patch binary section handling.
159
    def test_parse_binary_after_normal(self):
160
        patches = parse_patches(self.data_lines("binary-after-normal.patch"))
161
        self.assertIs(BinaryPatch, patches[1].__class__)
162
        self.assertIs(Patch, patches[0].__class__)
163
        self.assertContainsRe(patches[1].oldname, '^bar\t')
164
        self.assertContainsRe(patches[1].newname, '^qux\t')
165
        self.assertContainsRe(str(patches[1]),
166
                                  'Binary files bar\t.* and qux\t.* differ\n')
167
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
168
    def test_roundtrip_binary(self):
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
169
        patchtext = ''.join(self.data_lines("binary.patch"))
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
170
        patches = parse_patches(patchtext.splitlines(True))
171
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
4634.80.1 by Aaron Bentley
Parse binary files.
172
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
173
    def testInit(self):
174
        """Handle patches missing half the position, range tuple"""
175
        patchtext = \
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
176
"""--- orig/__vavg__.cl
177
+++ mod/__vavg__.cl
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
178
@@ -1 +1,2 @@
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
179
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
180
+__qbp__ = Na nygreangr Nepu pbzznaqyvar vagresnpr
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
181
"""
182
        self.compare_parsed(patchtext)
183
184
    def testLineLookup(self):
185
        import sys
186
        """Make sure we can accurately look up mod line from orig"""
187
        patch = parse_patch(self.datafile("diff"))
188
        orig = list(self.datafile("orig"))
189
        mod = list(self.datafile("mod"))
190
        removals = []
191
        for i in range(len(orig)):
192
            mod_pos = patch.pos_in_mod(i)
193
            if mod_pos is None:
194
                removals.append(orig[i])
195
                continue
3376.2.5 by Martin Pool
Remove a few more elusive assert statements
196
            self.assertEqual(mod[mod_pos], orig[i])
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
197
        rem_iter = removals.__iter__()
198
        for hunk in patch.hunks:
199
            for line in hunk.lines:
200
                if isinstance(line, RemoveLine):
201
                    next = rem_iter.next()
202
                    if line.contents != next:
203
                        sys.stdout.write(" orig:%spatch:%s" % (next,
204
                                         line.contents))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
205
                    self.assertEqual(line.contents, next)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
206
        self.assertRaises(StopIteration, rem_iter.next)
207
208
    def testPatching(self):
209
        """Test a few patch files, and make sure they work."""
210
        files = [
211
            ('diff-2', 'orig-2', 'mod-2'),
212
            ('diff-3', 'orig-3', 'mod-3'),
213
            ('diff-4', 'orig-4', 'mod-4'),
214
            ('diff-5', 'orig-5', 'mod-5'),
215
            ('diff-6', 'orig-6', 'mod-6'),
3873.1.4 by Benoît Pierre
Add another test for patches to check we correctly handle '\ No newline
216
            ('diff-7', 'orig-7', 'mod-7'),
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
217
        ]
218
        for diff, orig, mod in files:
219
            patch = self.datafile(diff)
220
            orig_lines = list(self.datafile(orig))
221
            mod_lines = list(self.datafile(mod))
222
223
            patched_file = IterableFile(iter_patched(orig_lines, patch))
224
            lines = []
225
            count = 0
226
            for patch_line in patched_file:
227
                self.assertEqual(patch_line, mod_lines[count])
228
                count += 1
229
            self.assertEqual(count, len(mod_lines))
230
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
231
    def test_iter_patched_binary(self):
232
        binary_lines = self.data_lines('binary.patch')
233
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
234
235
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
236
    def test_iter_patched_from_hunks(self):
237
        """Test a few patch files, and make sure they work."""
238
        files = [
239
            ('diff-2', 'orig-2', 'mod-2'),
240
            ('diff-3', 'orig-3', 'mod-3'),
241
            ('diff-4', 'orig-4', 'mod-4'),
242
            ('diff-5', 'orig-5', 'mod-5'),
243
            ('diff-6', 'orig-6', 'mod-6'),
3873.1.4 by Benoît Pierre
Add another test for patches to check we correctly handle '\ No newline
244
            ('diff-7', 'orig-7', 'mod-7'),
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
245
        ]
246
        for diff, orig, mod in files:
247
            parsed = parse_patch(self.datafile(diff))
248
            orig_lines = list(self.datafile(orig))
249
            mod_lines = list(self.datafile(mod))
250
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
251
            patched_file = IterableFile(iter_patched)
252
            lines = []
253
            count = 0
254
            for patch_line in patched_file:
255
                self.assertEqual(patch_line, mod_lines[count])
256
                count += 1
257
            self.assertEqual(count, len(mod_lines))
258
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
259
    def testFirstLineRenumber(self):
260
        """Make sure we handle lines at the beginning of the hunk"""
261
        patch = parse_patch(self.datafile("insert_top.patch"))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
262
        self.assertEqual(patch.pos_in_mod(0), 1)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
263
2298.6.1 by Johan Dahlberg
Fix bzrtools shelve command for removed lines beginning with "--"
264
    def testParsePatches(self):
265
        """Make sure file names can be extracted from tricky unified diffs"""
266
        patchtext = \
267
"""--- orig-7
268
+++ mod-7
269
@@ -1,10 +1,10 @@
270
 -- a
271
--- b
272
+++ c
273
 xx d
274
 xx e
275
 ++ f
276
-++ g
277
+-- h
278
 xx i
279
 xx j
280
 -- k
281
--- l
282
+++ m
283
--- orig-8
284
+++ mod-8
285
@@ -1 +1 @@
286
--- A
287
+++ B
288
@@ -1 +1 @@
289
--- C
290
+++ D
291
"""
292
        filenames = [('orig-7', 'mod-7'),
293
                     ('orig-8', 'mod-8')]
294
        patches = parse_patches(patchtext.splitlines(True))
295
        patch_files = []
296
        for patch in patches:
297
            patch_files.append((patch.oldname, patch.newname))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
298
        self.assertEqual(patch_files, filenames)
3946.4.1 by Tim Penhey
Extract out the counting of the stats values.
299
300
    def testStatsValues(self):
301
        """Test the added, removed and hunks values for stats_values."""
302
        patch = parse_patch(self.datafile("diff"))
303
        self.assertEqual((299, 407, 48), patch.stats_values())