~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2005-2010 Aaron Bentley, Canonical Ltd
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
2
#
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.82.127 by Aaron Bentley
PEP8 updates
16
17
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
18
import os.path
19
3376.2.6 by Martin Pool
Make PatchesTester use bzrlib TestCase base
20
from bzrlib.tests import TestCase
21
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
22
from bzrlib.iterablefile import IterableFile
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
23
from bzrlib.patches import (MalformedLine,
24
                            MalformedHunkHeader,
25
                            MalformedPatchHeader,
4634.80.1 by Aaron Bentley
Parse binary files.
26
                            BinaryPatch,
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
27
                            BinaryFiles,
4634.80.1 by Aaron Bentley
Parse binary files.
28
                            Patch,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
29
                            ContextLine,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
30
                            InsertLine,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
31
                            RemoveLine,
32
                            difference_index,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
33
                            get_patch_names,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
34
                            hunk_from_header,
35
                            iter_patched,
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
36
                            iter_patched_from_hunks,
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
37
                            parse_line,
2298.6.1 by Johan Dahlberg
Fix bzrtools shelve command for removed lines beginning with "--"
38
                            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.
39
                            parse_patches,
40
                            NO_NL)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
41
1185.82.127 by Aaron Bentley
PEP8 updates
42
3376.2.6 by Martin Pool
Make PatchesTester use bzrlib TestCase base
43
class PatchesTester(TestCase):
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
44
1185.82.7 by John Arbash Meinel
Adding patches.py into bzrlib, including the tests into the test suite.
45
    def datafile(self, filename):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
46
        data_path = os.path.join(os.path.dirname(__file__),
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
47
                                 "test_patches_data", filename)
1185.82.7 by John Arbash Meinel
Adding patches.py into bzrlib, including the tests into the test suite.
48
        return file(data_path, "rb")
49
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
50
    def data_lines(self, filename):
51
        datafile = self.datafile(filename)
52
        try:
53
            return datafile.readlines()
54
        finally:
55
            datafile.close()
56
5016.3.2 by Martin Pool
Add test that parse_patches(.. allow_dirty=True) works
57
    def test_parse_patches_leading_noise(self):
5243.1.2 by Martin
Point launchpad links in comments at production server rather than edge
58
        # https://bugs.launchpad.net/bzr/+bug/502076
59
        # https://code.launchpad.net/~toshio/bzr/allow-dirty-patches/+merge/18854
5016.3.2 by Martin Pool
Add test that parse_patches(.. allow_dirty=True) works
60
        lines = ["diff -pruN commands.py",
61
            "--- orig/commands.py",
62
            "+++ mod/dommands.py"]
63
        bits = parse_patches(iter(lines), allow_dirty=True)
64
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
65
    def testValidPatchHeader(self):
66
        """Parse a valid patch header"""
67
        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
68
        (orig, mod) = get_patch_names(lines.__iter__())
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
69
        self.assertEqual(orig, "orig/commands.py")
70
        self.assertEqual(mod, "mod/dommands.py")
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
71
72
    def testInvalidPatchHeader(self):
73
        """Parse an invalid patch header"""
74
        lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
75
        self.assertRaises(MalformedPatchHeader, get_patch_names,
76
                          lines.__iter__())
77
78
    def testValidHunkHeader(self):
79
        """Parse a valid hunk header"""
80
        header = "@@ -34,11 +50,6 @@\n"
81
        hunk = hunk_from_header(header);
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
82
        self.assertEqual(hunk.orig_pos, 34)
83
        self.assertEqual(hunk.orig_range, 11)
84
        self.assertEqual(hunk.mod_pos, 50)
85
        self.assertEqual(hunk.mod_range, 6)
86
        self.assertEqual(str(hunk), header)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
87
88
    def testValidHunkHeader2(self):
89
        """Parse a tricky, valid hunk header"""
90
        header = "@@ -1 +0,0 @@\n"
91
        hunk = hunk_from_header(header);
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
92
        self.assertEqual(hunk.orig_pos, 1)
93
        self.assertEqual(hunk.orig_range, 1)
94
        self.assertEqual(hunk.mod_pos, 0)
95
        self.assertEqual(hunk.mod_range, 0)
96
        self.assertEqual(str(hunk), header)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
97
1551.18.6 by Aaron Bentley
Add support for diff -p-style diffs to patch parser
98
    def testPDiff(self):
99
        """Parse a hunk header produced by diff -p"""
100
        header = "@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
101
        hunk = hunk_from_header(header)
102
        self.assertEqual('bzr 0.18rc1  2007-07-10', hunk.tail)
103
        self.assertEqual(header, str(hunk))
104
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
105
    def makeMalformed(self, header):
106
        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
107
108
    def testInvalidHeader(self):
109
        """Parse an invalid hunk header"""
110
        self.makeMalformed(" -34,11 +50,6 \n")
111
        self.makeMalformed("@@ +50,6 -34,11 @@\n")
112
        self.makeMalformed("@@ -34,11 +50,6 @@")
113
        self.makeMalformed("@@ -34.5,11 +50,6 @@\n")
114
        self.makeMalformed("@@-34,11 +50,6@@\n")
115
        self.makeMalformed("@@ 34,11 50,6 @@\n")
116
        self.makeMalformed("@@ -34,11 @@\n")
117
        self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
118
        self.makeMalformed("@@ -34,11 +50,-6 @@\n")
119
120
    def lineThing(self,text, type):
121
        line = parse_line(text)
3376.2.5 by Martin Pool
Remove a few more elusive assert statements
122
        self.assertIsInstance(line, type)
123
        self.assertEqual(str(line), text)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
124
125
    def makeMalformedLine(self, text):
126
        self.assertRaises(MalformedLine, parse_line, text)
127
128
    def testValidLine(self):
129
        """Parse a valid hunk line"""
130
        self.lineThing(" hello\n", ContextLine)
131
        self.lineThing("+hello\n", InsertLine)
132
        self.lineThing("-hello\n", RemoveLine)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
133
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
134
    def testMalformedLine(self):
135
        """Parse invalid valid hunk lines"""
136
        self.makeMalformedLine("hello\n")
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
137
3873.1.2 by Benoît Pierre
Add patches test for 'No newline at end of file' in the middle of hunk lines.
138
    def testMalformedLineNO_NL(self):
3873.1.4 by Benoît Pierre
Add another test for patches to check we correctly handle '\ No newline
139
        """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.
140
        self.makeMalformedLine(NO_NL)
141
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
142
    def compare_parsed(self, patchtext):
143
        lines = patchtext.splitlines(True)
144
        patch = parse_patch(lines.__iter__())
145
        pstr = str(patch)
146
        i = difference_index(patchtext, pstr)
147
        if i is not None:
148
            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
149
        self.assertEqual (patchtext, str(patch))
150
151
    def testAll(self):
152
        """Test parsing a whole patch"""
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
153
        patchtext = self.datafile("patchtext.patch").read()
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
154
        self.compare_parsed(patchtext)
155
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
156
    def test_parse_binary(self):
4634.80.1 by Aaron Bentley
Parse binary files.
157
        """Test parsing a whole patch"""
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
158
        patches = parse_patches(self.data_lines("binary.patch"))
4634.80.1 by Aaron Bentley
Parse binary files.
159
        self.assertIs(BinaryPatch, patches[0].__class__)
160
        self.assertIs(Patch, patches[1].__class__)
161
        self.assertContainsRe(patches[0].oldname, '^bar\t')
162
        self.assertContainsRe(patches[0].newname, '^qux\t')
163
        self.assertContainsRe(str(patches[0]),
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
164
                                  'Binary files bar\t.* and qux\t.* differ\n')
165
4634.98.1 by Aaron Bentley
Improve patch binary section handling.
166
    def test_parse_binary_after_normal(self):
167
        patches = parse_patches(self.data_lines("binary-after-normal.patch"))
168
        self.assertIs(BinaryPatch, patches[1].__class__)
169
        self.assertIs(Patch, patches[0].__class__)
170
        self.assertContainsRe(patches[1].oldname, '^bar\t')
171
        self.assertContainsRe(patches[1].newname, '^qux\t')
172
        self.assertContainsRe(str(patches[1]),
173
                                  'Binary files bar\t.* and qux\t.* differ\n')
174
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
175
    def test_roundtrip_binary(self):
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
176
        patchtext = ''.join(self.data_lines("binary.patch"))
4634.80.2 by Aaron Bentley
Ensure patch roundtripping.
177
        patches = parse_patches(patchtext.splitlines(True))
178
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
4634.80.1 by Aaron Bentley
Parse binary files.
179
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
180
    def testInit(self):
181
        """Handle patches missing half the position, range tuple"""
182
        patchtext = \
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
183
"""--- orig/__vavg__.cl
184
+++ mod/__vavg__.cl
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
185
@@ -1 +1,2 @@
1185.82.129 by Aaron Bentley
Removed confusing text from the test module
186
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
187
+__qbp__ = Na nygreangr Nepu pbzznaqyvar vagresnpr
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
188
"""
189
        self.compare_parsed(patchtext)
190
191
    def testLineLookup(self):
192
        import sys
193
        """Make sure we can accurately look up mod line from orig"""
194
        patch = parse_patch(self.datafile("diff"))
195
        orig = list(self.datafile("orig"))
196
        mod = list(self.datafile("mod"))
197
        removals = []
198
        for i in range(len(orig)):
199
            mod_pos = patch.pos_in_mod(i)
200
            if mod_pos is None:
201
                removals.append(orig[i])
202
                continue
3376.2.5 by Martin Pool
Remove a few more elusive assert statements
203
            self.assertEqual(mod[mod_pos], orig[i])
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
204
        rem_iter = removals.__iter__()
205
        for hunk in patch.hunks:
206
            for line in hunk.lines:
207
                if isinstance(line, RemoveLine):
208
                    next = rem_iter.next()
209
                    if line.contents != next:
210
                        sys.stdout.write(" orig:%spatch:%s" % (next,
211
                                         line.contents))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
212
                    self.assertEqual(line.contents, next)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
213
        self.assertRaises(StopIteration, rem_iter.next)
214
215
    def testPatching(self):
216
        """Test a few patch files, and make sure they work."""
217
        files = [
218
            ('diff-2', 'orig-2', 'mod-2'),
219
            ('diff-3', 'orig-3', 'mod-3'),
220
            ('diff-4', 'orig-4', 'mod-4'),
221
            ('diff-5', 'orig-5', 'mod-5'),
222
            ('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
223
            ('diff-7', 'orig-7', 'mod-7'),
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
224
        ]
225
        for diff, orig, mod in files:
226
            patch = self.datafile(diff)
227
            orig_lines = list(self.datafile(orig))
228
            mod_lines = list(self.datafile(mod))
229
230
            patched_file = IterableFile(iter_patched(orig_lines, patch))
231
            lines = []
232
            count = 0
233
            for patch_line in patched_file:
234
                self.assertEqual(patch_line, mod_lines[count])
235
                count += 1
236
            self.assertEqual(count, len(mod_lines))
237
4634.80.3 by Aaron Bentley
Clean up tests, test iter_patched.
238
    def test_iter_patched_binary(self):
239
        binary_lines = self.data_lines('binary.patch')
240
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
241
242
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
243
    def test_iter_patched_from_hunks(self):
244
        """Test a few patch files, and make sure they work."""
245
        files = [
246
            ('diff-2', 'orig-2', 'mod-2'),
247
            ('diff-3', 'orig-3', 'mod-3'),
248
            ('diff-4', 'orig-4', 'mod-4'),
249
            ('diff-5', 'orig-5', 'mod-5'),
250
            ('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
251
            ('diff-7', 'orig-7', 'mod-7'),
3363.18.3 by Aaron Bentley
Add tests for iter_patched_from_hunks
252
        ]
253
        for diff, orig, mod in files:
254
            parsed = parse_patch(self.datafile(diff))
255
            orig_lines = list(self.datafile(orig))
256
            mod_lines = list(self.datafile(mod))
257
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
258
            patched_file = IterableFile(iter_patched)
259
            lines = []
260
            count = 0
261
            for patch_line in patched_file:
262
                self.assertEqual(patch_line, mod_lines[count])
263
                count += 1
264
            self.assertEqual(count, len(mod_lines))
265
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
266
    def testFirstLineRenumber(self):
267
        """Make sure we handle lines at the beginning of the hunk"""
268
        patch = parse_patch(self.datafile("insert_top.patch"))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
269
        self.assertEqual(patch.pos_in_mod(0), 1)
1185.82.126 by Aaron Bentley
Split the patch testing out into a separate file
270
2298.6.1 by Johan Dahlberg
Fix bzrtools shelve command for removed lines beginning with "--"
271
    def testParsePatches(self):
272
        """Make sure file names can be extracted from tricky unified diffs"""
273
        patchtext = \
274
"""--- orig-7
275
+++ mod-7
276
@@ -1,10 +1,10 @@
277
 -- a
278
--- b
279
+++ c
280
 xx d
281
 xx e
282
 ++ f
283
-++ g
284
+-- h
285
 xx i
286
 xx j
287
 -- k
288
--- l
289
+++ m
290
--- orig-8
291
+++ mod-8
292
@@ -1 +1 @@
293
--- A
294
+++ B
295
@@ -1 +1 @@
296
--- C
297
+++ D
298
"""
299
        filenames = [('orig-7', 'mod-7'),
300
                     ('orig-8', 'mod-8')]
301
        patches = parse_patches(patchtext.splitlines(True))
302
        patch_files = []
303
        for patch in patches:
304
            patch_files.append((patch.oldname, patch.newname))
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
305
        self.assertEqual(patch_files, filenames)
3946.4.1 by Tim Penhey
Extract out the counting of the stats values.
306
307
    def testStatsValues(self):
308
        """Test the added, removed and hunks values for stats_values."""
309
        patch = parse_patch(self.datafile("diff"))
310
        self.assertEqual((299, 407, 48), patch.stats_values())