/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_patches.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import os.path
19
19
 
20
 
from bzrlib.tests import TestCase
 
20
from breezy.tests import TestCase, TestCaseWithTransport
21
21
 
22
 
from bzrlib.iterablefile import IterableFile
23
 
from bzrlib.patches import (MalformedLine,
24
 
                            MalformedHunkHeader,
25
 
                            MalformedPatchHeader,
26
 
                            BinaryPatch,
27
 
                            BinaryFiles,
28
 
                            Patch,
29
 
                            ContextLine,
30
 
                            InsertLine,
31
 
                            RemoveLine,
32
 
                            difference_index,
33
 
                            get_patch_names,
34
 
                            hunk_from_header,
35
 
                            iter_patched,
36
 
                            iter_patched_from_hunks,
37
 
                            parse_line,
38
 
                            parse_patch,
39
 
                            parse_patches,
40
 
                            NO_NL)
 
22
from breezy.iterablefile import IterableFile
 
23
from breezy.patches import (
 
24
    AppliedPatches,
 
25
    MalformedLine,
 
26
    MalformedHunkHeader,
 
27
    MalformedPatchHeader,
 
28
    BinaryPatch,
 
29
    BinaryFiles,
 
30
    Patch,
 
31
    ContextLine,
 
32
    InsertLine,
 
33
    RemoveLine,
 
34
    difference_index,
 
35
    get_patch_names,
 
36
    hunk_from_header,
 
37
    iter_patched,
 
38
    iter_patched_from_hunks,
 
39
    parse_line,
 
40
    parse_patch,
 
41
    parse_patches,
 
42
    NO_NL,
 
43
    )
41
44
 
42
45
 
43
46
class PatchesTester(TestCase):
45
48
    def datafile(self, filename):
46
49
        data_path = os.path.join(os.path.dirname(__file__),
47
50
                                 "test_patches_data", filename)
48
 
        return file(data_path, "rb")
 
51
        return open(data_path, "rb")
49
52
 
50
53
    def data_lines(self, filename):
51
 
        datafile = self.datafile(filename)
52
 
        try:
 
54
        with self.datafile(filename) as datafile:
53
55
            return datafile.readlines()
54
 
        finally:
55
 
            datafile.close()
56
56
 
57
57
    def test_parse_patches_leading_noise(self):
58
 
        # https://bugs.edge.launchpad.net/bzr/+bug/502076
59
 
        # https://code.edge.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)
 
58
        # https://bugs.launchpad.net/bzr/+bug/502076
 
59
        # https://code.launchpad.net/~toshio/bzr/allow-dirty-patches/+merge/18854
 
60
        lines = [b"diff -pruN commands.py",
 
61
                 b"--- orig/commands.py",
 
62
                 b"+++ mod/dommands.py"]
 
63
        bits = list(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 = [b"=== added directory 'foo/bar'\n",
 
68
                 b"=== modified file 'orig/commands.py'\n",
 
69
                 b"--- orig/commands.py\n",
 
70
                 b"+++ mod/dommands.py\n",
 
71
                 b"=== modified file 'orig/another.py'\n",
 
72
                 b"--- orig/another.py\n",
 
73
                 b"+++ mod/another.py\n"]
 
74
        patches = list(parse_patches(
 
75
            lines.__iter__(), allow_dirty=True, keep_dirty=True))
 
76
        self.assertLength(2, patches)
 
77
        self.assertEqual(patches[0]['dirty_head'],
 
78
                         [b"=== added directory 'foo/bar'\n",
 
79
                          b"=== modified file 'orig/commands.py'\n"])
 
80
        self.assertEqual(patches[0]['patch'].get_header().splitlines(True),
 
81
                         [b"--- orig/commands.py\n", b"+++ mod/dommands.py\n"])
 
82
        self.assertEqual(patches[1]['dirty_head'],
 
83
                         [b"=== modified file 'orig/another.py'\n"])
 
84
        self.assertEqual(patches[1]['patch'].get_header().splitlines(True),
 
85
                         [b"--- orig/another.py\n", b"+++ mod/another.py\n"])
64
86
 
65
87
    def testValidPatchHeader(self):
66
88
        """Parse a valid patch header"""
67
 
        lines = "--- orig/commands.py\n+++ mod/dommands.py\n".split('\n')
 
89
        lines = b"--- orig/commands.py\n+++ mod/dommands.py\n".split(b'\n')
68
90
        (orig, mod) = get_patch_names(lines.__iter__())
69
 
        self.assertEqual(orig, "orig/commands.py")
70
 
        self.assertEqual(mod, "mod/dommands.py")
 
91
        self.assertEqual(orig, b"orig/commands.py")
 
92
        self.assertEqual(mod, b"mod/dommands.py")
71
93
 
72
94
    def testInvalidPatchHeader(self):
73
95
        """Parse an invalid patch header"""
74
 
        lines = "-- orig/commands.py\n+++ mod/dommands.py".split('\n')
 
96
        lines = b"-- orig/commands.py\n+++ mod/dommands.py".split(b'\n')
75
97
        self.assertRaises(MalformedPatchHeader, get_patch_names,
76
98
                          lines.__iter__())
77
99
 
78
100
    def testValidHunkHeader(self):
79
101
        """Parse a valid hunk header"""
80
 
        header = "@@ -34,11 +50,6 @@\n"
81
 
        hunk = hunk_from_header(header);
 
102
        header = b"@@ -34,11 +50,6 @@\n"
 
103
        hunk = hunk_from_header(header)
82
104
        self.assertEqual(hunk.orig_pos, 34)
83
105
        self.assertEqual(hunk.orig_range, 11)
84
106
        self.assertEqual(hunk.mod_pos, 50)
85
107
        self.assertEqual(hunk.mod_range, 6)
86
 
        self.assertEqual(str(hunk), header)
 
108
        self.assertEqual(hunk.as_bytes(), header)
87
109
 
88
110
    def testValidHunkHeader2(self):
89
111
        """Parse a tricky, valid hunk header"""
90
 
        header = "@@ -1 +0,0 @@\n"
91
 
        hunk = hunk_from_header(header);
 
112
        header = b"@@ -1 +0,0 @@\n"
 
113
        hunk = hunk_from_header(header)
92
114
        self.assertEqual(hunk.orig_pos, 1)
93
115
        self.assertEqual(hunk.orig_range, 1)
94
116
        self.assertEqual(hunk.mod_pos, 0)
95
117
        self.assertEqual(hunk.mod_range, 0)
96
 
        self.assertEqual(str(hunk), header)
 
118
        self.assertEqual(hunk.as_bytes(), header)
97
119
 
98
120
    def testPDiff(self):
99
121
        """Parse a hunk header produced by diff -p"""
100
 
        header = "@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
 
122
        header = b"@@ -407,7 +292,7 @@ bzr 0.18rc1  2007-07-10\n"
101
123
        hunk = hunk_from_header(header)
102
 
        self.assertEqual('bzr 0.18rc1  2007-07-10', hunk.tail)
103
 
        self.assertEqual(header, str(hunk))
 
124
        self.assertEqual(b'bzr 0.18rc1  2007-07-10', hunk.tail)
 
125
        self.assertEqual(header, hunk.as_bytes())
104
126
 
105
127
    def makeMalformed(self, header):
106
128
        self.assertRaises(MalformedHunkHeader, hunk_from_header, header)
107
129
 
108
130
    def testInvalidHeader(self):
109
131
        """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")
 
132
        self.makeMalformed(b" -34,11 +50,6 \n")
 
133
        self.makeMalformed(b"@@ +50,6 -34,11 @@\n")
 
134
        self.makeMalformed(b"@@ -34,11 +50,6 @@")
 
135
        self.makeMalformed(b"@@ -34.5,11 +50,6 @@\n")
 
136
        self.makeMalformed(b"@@-34,11 +50,6@@\n")
 
137
        self.makeMalformed(b"@@ 34,11 50,6 @@\n")
 
138
        self.makeMalformed(b"@@ -34,11 @@\n")
 
139
        self.makeMalformed(b"@@ -34,11 +50,6.5 @@\n")
 
140
        self.makeMalformed(b"@@ -34,11 +50,-6 @@\n")
119
141
 
120
 
    def lineThing(self,text, type):
 
142
    def lineThing(self, text, type):
121
143
        line = parse_line(text)
122
144
        self.assertIsInstance(line, type)
123
 
        self.assertEqual(str(line), text)
 
145
        self.assertEqual(line.as_bytes(), text)
124
146
 
125
147
    def makeMalformedLine(self, text):
126
148
        self.assertRaises(MalformedLine, parse_line, text)
127
149
 
128
150
    def testValidLine(self):
129
151
        """Parse a valid hunk line"""
130
 
        self.lineThing(" hello\n", ContextLine)
131
 
        self.lineThing("+hello\n", InsertLine)
132
 
        self.lineThing("-hello\n", RemoveLine)
 
152
        self.lineThing(b" hello\n", ContextLine)
 
153
        self.lineThing(b"+hello\n", InsertLine)
 
154
        self.lineThing(b"-hello\n", RemoveLine)
133
155
 
134
156
    def testMalformedLine(self):
135
157
        """Parse invalid valid hunk lines"""
136
 
        self.makeMalformedLine("hello\n")
 
158
        self.makeMalformedLine(b"hello\n")
137
159
 
138
160
    def testMalformedLineNO_NL(self):
139
 
        """Parse invalid '\ No newline at end of file' in hunk lines"""
 
161
        """Parse invalid '\\ No newline at end of file' in hunk lines"""
140
162
        self.makeMalformedLine(NO_NL)
141
163
 
142
164
    def compare_parsed(self, patchtext):
143
165
        lines = patchtext.splitlines(True)
144
166
        patch = parse_patch(lines.__iter__())
145
 
        pstr = str(patch)
 
167
        pstr = patch.as_bytes()
146
168
        i = difference_index(patchtext, pstr)
147
169
        if i is not None:
148
 
            print "%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i])
149
 
        self.assertEqual (patchtext, str(patch))
 
170
            print("%i: \"%s\" != \"%s\"" % (i, patchtext[i], pstr[i]))
 
171
        self.assertEqual(patchtext, patch.as_bytes())
150
172
 
151
173
    def testAll(self):
152
174
        """Test parsing a whole patch"""
153
 
        patchtext = self.datafile("patchtext.patch").read()
 
175
        with self.datafile("patchtext.patch") as f:
 
176
            patchtext = f.read()
154
177
        self.compare_parsed(patchtext)
155
178
 
156
179
    def test_parse_binary(self):
157
180
        """Test parsing a whole patch"""
158
 
        patches = parse_patches(self.data_lines("binary.patch"))
 
181
        patches = list(parse_patches(self.data_lines("binary.patch")))
159
182
        self.assertIs(BinaryPatch, patches[0].__class__)
160
183
        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]),
164
 
                                  'Binary files bar\t.* and qux\t.* differ\n')
 
184
        self.assertContainsRe(patches[0].oldname, b'^bar\t')
 
185
        self.assertContainsRe(patches[0].newname, b'^qux\t')
 
186
        self.assertContainsRe(patches[0].as_bytes(),
 
187
                              b'Binary files bar\t.* and qux\t.* differ\n')
165
188
 
166
189
    def test_parse_binary_after_normal(self):
167
 
        patches = parse_patches(self.data_lines("binary-after-normal.patch"))
 
190
        patches = list(parse_patches(
 
191
            self.data_lines("binary-after-normal.patch")))
168
192
        self.assertIs(BinaryPatch, patches[1].__class__)
169
193
        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')
 
194
        self.assertContainsRe(patches[1].oldname, b'^bar\t')
 
195
        self.assertContainsRe(patches[1].newname, b'^qux\t')
 
196
        self.assertContainsRe(patches[1].as_bytes(),
 
197
                              b'Binary files bar\t.* and qux\t.* differ\n')
174
198
 
175
199
    def test_roundtrip_binary(self):
176
 
        patchtext = ''.join(self.data_lines("binary.patch"))
 
200
        patchtext = b''.join(self.data_lines("binary.patch"))
177
201
        patches = parse_patches(patchtext.splitlines(True))
178
 
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
 
202
        self.assertEqual(patchtext, b''.join(p.as_bytes() for p in patches))
179
203
 
180
204
    def testInit(self):
181
205
        """Handle patches missing half the position, range tuple"""
182
206
        patchtext = \
183
 
"""--- orig/__vavg__.cl
 
207
            b"""--- orig/__vavg__.cl
184
208
+++ mod/__vavg__.cl
185
209
@@ -1 +1,2 @@
186
210
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
189
213
        self.compare_parsed(patchtext)
190
214
 
191
215
    def testLineLookup(self):
192
 
        import sys
193
216
        """Make sure we can accurately look up mod line from orig"""
194
217
        patch = parse_patch(self.datafile("diff"))
195
218
        orig = list(self.datafile("orig"))
205
228
        for hunk in patch.hunks:
206
229
            for line in hunk.lines:
207
230
                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))
212
 
                    self.assertEqual(line.contents, next)
213
 
        self.assertRaises(StopIteration, rem_iter.next)
 
231
                    self.assertEqual(line.contents, next(rem_iter))
 
232
        self.assertRaises(StopIteration, next, rem_iter)
214
233
 
215
234
    def testPatching(self):
216
235
        """Test a few patch files, and make sure they work."""
228
247
            mod_lines = list(self.datafile(mod))
229
248
 
230
249
            patched_file = IterableFile(iter_patched(orig_lines, patch))
231
 
            lines = []
232
250
            count = 0
233
251
            for patch_line in patched_file:
234
252
                self.assertEqual(patch_line, mod_lines[count])
239
257
        binary_lines = self.data_lines('binary.patch')
240
258
        e = self.assertRaises(BinaryFiles, iter_patched, [], binary_lines)
241
259
 
242
 
 
243
260
    def test_iter_patched_from_hunks(self):
244
261
        """Test a few patch files, and make sure they work."""
245
262
        files = [
256
273
            mod_lines = list(self.datafile(mod))
257
274
            iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
258
275
            patched_file = IterableFile(iter_patched)
259
 
            lines = []
260
276
            count = 0
261
277
            for patch_line in patched_file:
262
278
                self.assertEqual(patch_line, mod_lines[count])
271
287
    def testParsePatches(self):
272
288
        """Make sure file names can be extracted from tricky unified diffs"""
273
289
        patchtext = \
274
 
"""--- orig-7
 
290
            b"""--- orig-7
275
291
+++ mod-7
276
292
@@ -1,10 +1,10 @@
277
293
 -- a
296
312
--- C
297
313
+++ D
298
314
"""
299
 
        filenames = [('orig-7', 'mod-7'),
300
 
                     ('orig-8', 'mod-8')]
 
315
        filenames = [(b'orig-7', b'mod-7'),
 
316
                     (b'orig-8', b'mod-8')]
301
317
        patches = parse_patches(patchtext.splitlines(True))
302
318
        patch_files = []
303
319
        for patch in patches:
308
324
        """Test the added, removed and hunks values for stats_values."""
309
325
        patch = parse_patch(self.datafile("diff"))
310
326
        self.assertEqual((299, 407, 48), patch.stats_values())
 
327
 
 
328
 
 
329
class AppliedPatchesTests(TestCaseWithTransport):
 
330
 
 
331
    def test_apply_simple(self):
 
332
        tree = self.make_branch_and_tree('.')
 
333
        self.build_tree_contents([('a', 'a\n')])
 
334
        tree.add('a')
 
335
        tree.commit('Add a')
 
336
        patch = parse_patch(b"""\
 
337
--- a/a
 
338
+++ a/a
 
339
@@ -1 +1 @@
 
340
-a
 
341
+b
 
342
""".splitlines(True))
 
343
        with AppliedPatches(tree, [patch]) as newtree:
 
344
            self.assertEqual(b'b\n', newtree.get_file_text('a'))
 
345
 
 
346
    def test_apply_delete(self):
 
347
        tree = self.make_branch_and_tree('.')
 
348
        self.build_tree_contents([('a', 'a\n')])
 
349
        tree.add('a')
 
350
        tree.commit('Add a')
 
351
        patch = parse_patch(b"""\
 
352
--- a/a
 
353
+++ /dev/null
 
354
@@ -1 +0,0 @@
 
355
-a
 
356
""".splitlines(True))
 
357
        with AppliedPatches(tree, [patch]) as newtree:
 
358
            self.assertFalse(newtree.has_filename('a'))
 
359
 
 
360
    def test_apply_add(self):
 
361
        tree = self.make_branch_and_tree('.')
 
362
        self.build_tree_contents([('a', 'a\n')])
 
363
        tree.add('a')
 
364
        tree.commit('Add a')
 
365
        patch = parse_patch(b"""\
 
366
--- /dev/null
 
367
+++ a/b
 
368
@@ -0,0 +1 @@
 
369
+b
 
370
""".splitlines(True))
 
371
        with AppliedPatches(tree, [patch]) as newtree:
 
372
            self.assertEqual(b'b\n', newtree.get_file_text('b'))