~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_patches.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

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
38
                            parse_patch,
35
 
                            parse_patches)
36
 
 
37
 
 
38
 
class PatchesTester(unittest.TestCase):
 
39
                            parse_patches,
 
40
                            NO_NL)
 
41
 
 
42
 
 
43
class PatchesTester(TestCase):
 
44
 
39
45
    def datafile(self, filename):
40
 
        data_path = os.path.join(os.path.dirname(__file__), 
 
46
        data_path = os.path.join(os.path.dirname(__file__),
41
47
                                 "test_patches_data", filename)
42
48
        return file(data_path, "rb")
43
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
                 "=== modified file 'orig/another.py'\n",
 
72
                 "--- orig/another.py\n",
 
73
                 "+++ mod/another.py\n"]
 
74
        patches = parse_patches(
 
75
            lines.__iter__(), allow_dirty=True, keep_dirty=True)
 
76
        self.assertLength(2, patches)
 
77
        self.assertEqual(patches[0]['dirty_head'],
 
78
                         ["=== added directory 'foo/bar'\n",
 
79
                          "=== modified file 'orig/commands.py'\n"])
 
80
        self.assertEqual(patches[0]['patch'].get_header().splitlines(True),
 
81
                         ["--- orig/commands.py\n", "+++ mod/dommands.py\n"])
 
82
        self.assertEqual(patches[1]['dirty_head'],
 
83
                         ["=== modified file 'orig/another.py'\n"])
 
84
        self.assertEqual(patches[1]['patch'].get_header().splitlines(True),
 
85
                         ["--- orig/another.py\n", "+++ mod/another.py\n"])
 
86
 
44
87
    def testValidPatchHeader(self):
45
88
        """Parse a valid patch header"""
46
89
        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
47
90
        (orig, mod) = get_patch_names(lines.__iter__())
48
 
        assert(orig == "orig/commands.py")
49
 
        assert(mod == "mod/dommands.py")
 
91
        self.assertEqual(orig, "orig/commands.py")
 
92
        self.assertEqual(mod, "mod/dommands.py")
50
93
 
51
94
    def testInvalidPatchHeader(self):
52
95
        """Parse an invalid patch header"""
57
100
    def testValidHunkHeader(self):
58
101
        """Parse a valid hunk header"""
59
102
        header = "@@ -34,11 +50,6 @@\n"
60
 
        hunk = hunk_from_header(header);
61
 
        assert (hunk.orig_pos == 34)
62
 
        assert (hunk.orig_range == 11)
63
 
        assert (hunk.mod_pos == 50)
64
 
        assert (hunk.mod_range == 6)
65
 
        assert (str(hunk) == header)
 
103
        hunk = hunk_from_header(header)
 
104
        self.assertEqual(hunk.orig_pos, 34)
 
105
        self.assertEqual(hunk.orig_range, 11)
 
106
        self.assertEqual(hunk.mod_pos, 50)
 
107
        self.assertEqual(hunk.mod_range, 6)
 
108
        self.assertEqual(str(hunk), header)
66
109
 
67
110
    def testValidHunkHeader2(self):
68
111
        """Parse a tricky, valid hunk header"""
69
112
        header = "@@ -1 +0,0 @@\n"
70
 
        hunk = hunk_from_header(header);
71
 
        assert (hunk.orig_pos == 1)
72
 
        assert (hunk.orig_range == 1)
73
 
        assert (hunk.mod_pos == 0)
74
 
        assert (hunk.mod_range == 0)
75
 
        assert (str(hunk) == header)
 
113
        hunk = hunk_from_header(header)
 
114
        self.assertEqual(hunk.orig_pos, 1)
 
115
        self.assertEqual(hunk.orig_range, 1)
 
116
        self.assertEqual(hunk.mod_pos, 0)
 
117
        self.assertEqual(hunk.mod_range, 0)
 
118
        self.assertEqual(str(hunk), header)
76
119
 
77
120
    def testPDiff(self):
78
121
        """Parse a hunk header produced by diff -p"""
96
139
        self.makeMalformed("@@ -34,11 +50,6.5 @@\n")
97
140
        self.makeMalformed("@@ -34,11 +50,-6 @@\n")
98
141
 
99
 
    def lineThing(self,text, type):
 
142
    def lineThing(self, text, type):
100
143
        line = parse_line(text)
101
 
        assert(isinstance(line, type))
102
 
        assert(str(line)==text)
 
144
        self.assertIsInstance(line, type)
 
145
        self.assertEqual(str(line), text)
103
146
 
104
147
    def makeMalformedLine(self, text):
105
148
        self.assertRaises(MalformedLine, parse_line, text)
109
152
        self.lineThing(" hello\n", ContextLine)
110
153
        self.lineThing("+hello\n", InsertLine)
111
154
        self.lineThing("-hello\n", RemoveLine)
112
 
    
 
155
 
113
156
    def testMalformedLine(self):
114
157
        """Parse invalid valid hunk lines"""
115
158
        self.makeMalformedLine("hello\n")
116
 
    
 
159
 
 
160
    def testMalformedLineNO_NL(self):
 
161
        """Parse invalid '\ No newline at end of file' in hunk lines"""
 
162
        self.makeMalformedLine(NO_NL)
 
163
 
117
164
    def compare_parsed(self, patchtext):
118
165
        lines = patchtext.splitlines(True)
119
166
        patch = parse_patch(lines.__iter__())
121
168
        i = difference_index(patchtext, pstr)
122
169
        if i is not None:
123
170
            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
124
 
        self.assertEqual (patchtext, str(patch))
 
171
        self.assertEqual(patchtext, str(patch))
125
172
 
126
173
    def testAll(self):
127
174
        """Test parsing a whole patch"""
128
175
        patchtext = self.datafile("patchtext.patch").read()
129
176
        self.compare_parsed(patchtext)
130
177
 
 
178
    def test_parse_binary(self):
 
179
        """Test parsing a whole patch"""
 
180
        patches = parse_patches(self.data_lines("binary.patch"))
 
181
        self.assertIs(BinaryPatch, patches[0].__class__)
 
182
        self.assertIs(Patch, patches[1].__class__)
 
183
        self.assertContainsRe(patches[0].oldname, '^bar\t')
 
184
        self.assertContainsRe(patches[0].newname, '^qux\t')
 
185
        self.assertContainsRe(str(patches[0]),
 
186
                              'Binary files bar\t.* and qux\t.* differ\n')
 
187
 
 
188
    def test_parse_binary_after_normal(self):
 
189
        patches = parse_patches(self.data_lines("binary-after-normal.patch"))
 
190
        self.assertIs(BinaryPatch, patches[1].__class__)
 
191
        self.assertIs(Patch, patches[0].__class__)
 
192
        self.assertContainsRe(patches[1].oldname, '^bar\t')
 
193
        self.assertContainsRe(patches[1].newname, '^qux\t')
 
194
        self.assertContainsRe(str(patches[1]),
 
195
                              'Binary files bar\t.* and qux\t.* differ\n')
 
196
 
 
197
    def test_roundtrip_binary(self):
 
198
        patchtext = ''.join(self.data_lines("binary.patch"))
 
199
        patches = parse_patches(patchtext.splitlines(True))
 
200
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
 
201
 
131
202
    def testInit(self):
132
203
        """Handle patches missing half the position, range tuple"""
133
204
        patchtext = \
151
222
            if mod_pos is None:
152
223
                removals.append(orig[i])
153
224
                continue
154
 
            assert(mod[mod_pos]==orig[i])
 
225
            self.assertEqual(mod[mod_pos], orig[i])
155
226
        rem_iter = removals.__iter__()
156
227
        for hunk in patch.hunks:
157
228
            for line in hunk.lines:
160
231
                    if line.contents != next:
161
232
                        sys.stdout.write(" orig:%spatch:%s" % (next,
162
233
                                         line.contents))
163
 
                    assert(line.contents == next)
 
234
                    self.assertEqual(line.contents, next)
164
235
        self.assertRaises(StopIteration, rem_iter.next)
165
236
 
166
237
    def testPatching(self):
171
242
            ('diff-4', 'orig-4', 'mod-4'),
172
243
            ('diff-5', 'orig-5', 'mod-5'),
173
244
            ('diff-6', 'orig-6', 'mod-6'),
 
245
            ('diff-7', 'orig-7', 'mod-7'),
174
246
        ]
175
247
        for diff, orig, mod in files:
176
248
            patch = self.datafile(diff)
178
250
            mod_lines = list(self.datafile(mod))
179
251
 
180
252
            patched_file = IterableFile(iter_patched(orig_lines, patch))
181
 
            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
 
 
259
    def test_iter_patched_binary(self):
 
260
        binary_lines = self.data_lines('binary.patch')
 
261
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
 
262
 
 
263
    def test_iter_patched_from_hunks(self):
 
264
        """Test a few patch files, and make sure they work."""
 
265
        files = [
 
266
            ('diff-2', 'orig-2', 'mod-2'),
 
267
            ('diff-3', 'orig-3', 'mod-3'),
 
268
            ('diff-4', 'orig-4', 'mod-4'),
 
269
            ('diff-5', 'orig-5', 'mod-5'),
 
270
            ('diff-6', 'orig-6', 'mod-6'),
 
271
            ('diff-7', 'orig-7', 'mod-7'),
 
272
        ]
 
273
        for diff, orig, mod in files:
 
274
            parsed = parse_patch(self.datafile(diff))
 
275
            orig_lines = list(self.datafile(orig))
 
276
            mod_lines = list(self.datafile(mod))
 
277
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
 
278
            patched_file = IterableFile(iter_patched)
182
279
            count = 0
183
280
            for patch_line in patched_file:
184
281
                self.assertEqual(patch_line, mod_lines[count])
188
285
    def testFirstLineRenumber(self):
189
286
        """Make sure we handle lines at the beginning of the hunk"""
190
287
        patch = parse_patch(self.datafile("insert_top.patch"))
191
 
        assert (patch.pos_in_mod(0)==1)
 
288
        self.assertEqual(patch.pos_in_mod(0), 1)
192
289
 
193
290
    def testParsePatches(self):
194
291
        """Make sure file names can be extracted from tricky unified diffs"""
224
321
        patch_files = []
225
322
        for patch in patches:
226
323
            patch_files.append((patch.oldname, patch.newname))
227
 
        assert (patch_files == filenames)
228
 
            
229
 
def test():
230
 
    patchesTestSuite = unittest.makeSuite(PatchesTester,'test')
231
 
    runner = unittest.TextTestRunner(verbosity=0)
232
 
    return runner.run(patchesTestSuite)
233
 
 
234
 
 
235
 
if __name__ == "__main__":
236
 
    test()
 
324
        self.assertEqual(patch_files, filenames)
 
325
 
 
326
    def testStatsValues(self):
 
327
        """Test the added, removed and hunks values for stats_values."""
 
328
        patch = parse_patch(self.datafile("diff"))
 
329
        self.assertEqual((299, 407, 48), patch.stats_values())