~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_patches.py

(vila) Fix test failures blocking package builds. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004 - 2006 Aaron Bentley, Canonical Ltd
2
 
# <aaron.bentley@utoronto.ca>
 
1
# Copyright (C) 2005-2010 Aaron Bentley, Canonical Ltd
3
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
13
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
 
18
 
 
19
 
import unittest
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
20
18
import os.path
21
19
 
 
20
from bzrlib.tests import TestCase
 
21
 
22
22
from bzrlib.iterablefile import IterableFile
23
 
from bzrlib.patches import (MalformedLine, 
24
 
                            MalformedHunkHeader, 
25
 
                            MalformedPatchHeader, 
26
 
                            ContextLine, 
 
23
from bzrlib.patches import (MalformedLine,
 
24
                            MalformedHunkHeader,
 
25
                            MalformedPatchHeader,
 
26
                            BinaryPatch,
 
27
                            BinaryFiles,
 
28
                            Patch,
 
29
                            ContextLine,
27
30
                            InsertLine,
28
 
                            RemoveLine, 
29
 
                            difference_index, 
 
31
                            RemoveLine,
 
32
                            difference_index,
30
33
                            get_patch_names,
31
 
                            hunk_from_header, 
32
 
                            iter_patched, 
 
34
                            hunk_from_header,
 
35
                            iter_patched,
 
36
                            iter_patched_from_hunks,
33
37
                            parse_line,
34
 
                            parse_patch)
35
 
 
36
 
 
37
 
class PatchesTester(unittest.TestCase):
 
38
                            parse_patch,
 
39
                            parse_patches,
 
40
                            NO_NL)
 
41
 
 
42
 
 
43
class PatchesTester(TestCase):
 
44
 
38
45
    def datafile(self, filename):
39
 
        data_path = os.path.join(os.path.dirname(__file__), 
 
46
        data_path = os.path.join(os.path.dirname(__file__),
40
47
                                 "test_patches_data", filename)
41
48
        return file(data_path, "rb")
42
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
        patches = parse_patches(
 
72
            lines.__iter__(), allow_dirty=True, keep_dirty=True)
 
73
        self.assertEqual(patches[0]['dirty_head'],
 
74
                         ["=== added directory 'foo/bar'\n",
 
75
                          "=== modified file 'orig/commands.py'\n"])
 
76
        self.assertEqual(patches[0]['patch'].get_header().splitlines(True),
 
77
                         ["--- orig/commands.py\n", "+++ mod/dommands.py\n"])
 
78
 
43
79
    def testValidPatchHeader(self):
44
80
        """Parse a valid patch header"""
45
81
        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
46
82
        (orig, mod) = get_patch_names(lines.__iter__())
47
 
        assert(orig == "orig/commands.py")
48
 
        assert(mod == "mod/dommands.py")
 
83
        self.assertEqual(orig, "orig/commands.py")
 
84
        self.assertEqual(mod, "mod/dommands.py")
49
85
 
50
86
    def testInvalidPatchHeader(self):
51
87
        """Parse an invalid patch header"""
56
92
    def testValidHunkHeader(self):
57
93
        """Parse a valid hunk header"""
58
94
        header = "@@ -34,11 +50,6 @@\n"
59
 
        hunk = hunk_from_header(header);
60
 
        assert (hunk.orig_pos == 34)
61
 
        assert (hunk.orig_range == 11)
62
 
        assert (hunk.mod_pos == 50)
63
 
        assert (hunk.mod_range == 6)
64
 
        assert (str(hunk) == header)
 
95
        hunk = hunk_from_header(header)
 
96
        self.assertEqual(hunk.orig_pos, 34)
 
97
        self.assertEqual(hunk.orig_range, 11)
 
98
        self.assertEqual(hunk.mod_pos, 50)
 
99
        self.assertEqual(hunk.mod_range, 6)
 
100
        self.assertEqual(str(hunk), header)
65
101
 
66
102
    def testValidHunkHeader2(self):
67
103
        """Parse a tricky, valid hunk header"""
68
104
        header = "@@ -1 +0,0 @@\n"
69
 
        hunk = hunk_from_header(header);
70
 
        assert (hunk.orig_pos == 1)
71
 
        assert (hunk.orig_range == 1)
72
 
        assert (hunk.mod_pos == 0)
73
 
        assert (hunk.mod_range == 0)
74
 
        assert (str(hunk) == header)
 
105
        hunk = hunk_from_header(header)
 
106
        self.assertEqual(hunk.orig_pos, 1)
 
107
        self.assertEqual(hunk.orig_range, 1)
 
108
        self.assertEqual(hunk.mod_pos, 0)
 
109
        self.assertEqual(hunk.mod_range, 0)
 
110
        self.assertEqual(str(hunk), header)
 
111
 
 
112
    def testPDiff(self):
 
113
        """Parse a hunk header produced by diff -p"""
 
114
        header = "@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
 
115
        hunk = hunk_from_header(header)
 
116
        self.assertEqual('bzr 0.18rc1  2007-07-10', hunk.tail)
 
117
        self.assertEqual(header, str(hunk))
75
118
 
76
119
    def makeMalformed(self, header):
77
120
        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
88
131
        self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
89
132
        self.makeMalformed("@@ -34,11 +50,-6 @@\n")
90
133
 
91
 
    def lineThing(self,text, type):
 
134
    def lineThing(self, text, type):
92
135
        line = parse_line(text)
93
 
        assert(isinstance(line, type))
94
 
        assert(str(line)==text)
 
136
        self.assertIsInstance(line, type)
 
137
        self.assertEqual(str(line), text)
95
138
 
96
139
    def makeMalformedLine(self, text):
97
140
        self.assertRaises(MalformedLine, parse_line, text)
101
144
        self.lineThing(" hello\n", ContextLine)
102
145
        self.lineThing("+hello\n", InsertLine)
103
146
        self.lineThing("-hello\n", RemoveLine)
104
 
    
 
147
 
105
148
    def testMalformedLine(self):
106
149
        """Parse invalid valid hunk lines"""
107
150
        self.makeMalformedLine("hello\n")
108
 
    
 
151
 
 
152
    def testMalformedLineNO_NL(self):
 
153
        """Parse invalid '\ No newline at end of file' in hunk lines"""
 
154
        self.makeMalformedLine(NO_NL)
 
155
 
109
156
    def compare_parsed(self, patchtext):
110
157
        lines = patchtext.splitlines(True)
111
158
        patch = parse_patch(lines.__iter__())
113
160
        i = difference_index(patchtext, pstr)
114
161
        if i is not None:
115
162
            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
116
 
        self.assertEqual (patchtext, str(patch))
 
163
        self.assertEqual(patchtext, str(patch))
117
164
 
118
165
    def testAll(self):
119
166
        """Test parsing a whole patch"""
120
167
        patchtext = self.datafile("patchtext.patch").read()
121
168
        self.compare_parsed(patchtext)
122
169
 
 
170
    def test_parse_binary(self):
 
171
        """Test parsing a whole patch"""
 
172
        patches = parse_patches(self.data_lines("binary.patch"))
 
173
        self.assertIs(BinaryPatch, patches[0].__class__)
 
174
        self.assertIs(Patch, patches[1].__class__)
 
175
        self.assertContainsRe(patches[0].oldname, '^bar\t')
 
176
        self.assertContainsRe(patches[0].newname, '^qux\t')
 
177
        self.assertContainsRe(str(patches[0]),
 
178
                              'Binary files bar\t.* and qux\t.* differ\n')
 
179
 
 
180
    def test_parse_binary_after_normal(self):
 
181
        patches = parse_patches(self.data_lines("binary-after-normal.patch"))
 
182
        self.assertIs(BinaryPatch, patches[1].__class__)
 
183
        self.assertIs(Patch, patches[0].__class__)
 
184
        self.assertContainsRe(patches[1].oldname, '^bar\t')
 
185
        self.assertContainsRe(patches[1].newname, '^qux\t')
 
186
        self.assertContainsRe(str(patches[1]),
 
187
                              'Binary files bar\t.* and qux\t.* differ\n')
 
188
 
 
189
    def test_roundtrip_binary(self):
 
190
        patchtext = ''.join(self.data_lines("binary.patch"))
 
191
        patches = parse_patches(patchtext.splitlines(True))
 
192
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
 
193
 
123
194
    def testInit(self):
124
195
        """Handle patches missing half the position, range tuple"""
125
196
        patchtext = \
143
214
            if mod_pos is None:
144
215
                removals.append(orig[i])
145
216
                continue
146
 
            assert(mod[mod_pos]==orig[i])
 
217
            self.assertEqual(mod[mod_pos], orig[i])
147
218
        rem_iter = removals.__iter__()
148
219
        for hunk in patch.hunks:
149
220
            for line in hunk.lines:
152
223
                    if line.contents != next:
153
224
                        sys.stdout.write(" orig:%spatch:%s" % (next,
154
225
                                         line.contents))
155
 
                    assert(line.contents == next)
 
226
                    self.assertEqual(line.contents, next)
156
227
        self.assertRaises(StopIteration, rem_iter.next)
157
228
 
158
229
    def testPatching(self):
163
234
            ('diff-4', 'orig-4', 'mod-4'),
164
235
            ('diff-5', 'orig-5', 'mod-5'),
165
236
            ('diff-6', 'orig-6', 'mod-6'),
 
237
            ('diff-7', 'orig-7', 'mod-7'),
166
238
        ]
167
239
        for diff, orig, mod in files:
168
240
            patch = self.datafile(diff)
170
242
            mod_lines = list(self.datafile(mod))
171
243
 
172
244
            patched_file = IterableFile(iter_patched(orig_lines, patch))
173
 
            lines = []
 
245
            count = 0
 
246
            for patch_line in patched_file:
 
247
                self.assertEqual(patch_line, mod_lines[count])
 
248
                count += 1
 
249
            self.assertEqual(count, len(mod_lines))
 
250
 
 
251
    def test_iter_patched_binary(self):
 
252
        binary_lines = self.data_lines('binary.patch')
 
253
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
 
254
 
 
255
    def test_iter_patched_from_hunks(self):
 
256
        """Test a few patch files, and make sure they work."""
 
257
        files = [
 
258
            ('diff-2', 'orig-2', 'mod-2'),
 
259
            ('diff-3', 'orig-3', 'mod-3'),
 
260
            ('diff-4', 'orig-4', 'mod-4'),
 
261
            ('diff-5', 'orig-5', 'mod-5'),
 
262
            ('diff-6', 'orig-6', 'mod-6'),
 
263
            ('diff-7', 'orig-7', 'mod-7'),
 
264
        ]
 
265
        for diff, orig, mod in files:
 
266
            parsed = parse_patch(self.datafile(diff))
 
267
            orig_lines = list(self.datafile(orig))
 
268
            mod_lines = list(self.datafile(mod))
 
269
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
 
270
            patched_file = IterableFile(iter_patched)
174
271
            count = 0
175
272
            for patch_line in patched_file:
176
273
                self.assertEqual(patch_line, mod_lines[count])
180
277
    def testFirstLineRenumber(self):
181
278
        """Make sure we handle lines at the beginning of the hunk"""
182
279
        patch = parse_patch(self.datafile("insert_top.patch"))
183
 
        assert (patch.pos_in_mod(0)==1)
184
 
 
185
 
def test():
186
 
    patchesTestSuite = unittest.makeSuite(PatchesTester,'test')
187
 
    runner = unittest.TextTestRunner(verbosity=0)
188
 
    return runner.run(patchesTestSuite)
189
 
 
190
 
 
191
 
if __name__ == "__main__":
192
 
    test()
 
280
        self.assertEqual(patch.pos_in_mod(0), 1)
 
281
 
 
282
    def testParsePatches(self):
 
283
        """Make sure file names can be extracted from tricky unified diffs"""
 
284
        patchtext = \
 
285
"""--- orig-7
 
286
+++ mod-7
 
287
@@ -1,10 +1,10 @@
 
288
 -- a
 
289
--- b
 
290
+++ c
 
291
 xx d
 
292
 xx e
 
293
 ++ f
 
294
-++ g
 
295
+-- h
 
296
 xx i
 
297
 xx j
 
298
 -- k
 
299
--- l
 
300
+++ m
 
301
--- orig-8
 
302
+++ mod-8
 
303
@@ -1 +1 @@
 
304
--- A
 
305
+++ B
 
306
@@ -1 +1 @@
 
307
--- C
 
308
+++ D
 
309
"""
 
310
        filenames = [('orig-7', 'mod-7'),
 
311
                     ('orig-8', 'mod-8')]
 
312
        patches = parse_patches(patchtext.splitlines(True))
 
313
        patch_files = []
 
314
        for patch in patches:
 
315
            patch_files.append((patch.oldname, patch.newname))
 
316
        self.assertEqual(patch_files, filenames)
 
317
 
 
318
    def testStatsValues(self):
 
319
        """Test the added, removed and hunks values for stats_values."""
 
320
        patch = parse_patch(self.datafile("diff"))
 
321
        self.assertEqual((299, 407, 48), patch.stats_values())