~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_patches.py

  • Committer: John Arbash Meinel
  • Date: 2009-02-19 20:55:17 UTC
  • mto: (0.22.4 experimental)
  • mto: This revision was merged to the branch mainline in revision 4280.
  • Revision ID: john@arbash-meinel.com-20090219205517-drw89424koe6h1da
Play around a bit.

1) Empty texts are no-op inserted, to avoid ever trying to match against their text.
2) If we find a new file-id and the compressor is more than half full, we go
ahead and start a new compressor.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Aaron Bentley, Canonical Ltd
2
 
#
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
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
 
18
 
import os.path
19
 
 
20
 
from bzrlib.tests import TestCase
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)
41
 
 
42
 
 
43
 
class PatchesTester(TestCase):
44
 
 
45
 
    def datafile(self, filename):
46
 
        data_path = os.path.join(os.path.dirname(__file__),
47
 
                                 "test_patches_data", filename)
48
 
        return file(data_path, "rb")
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 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__())
69
 
        self.assertEqual(orig, "orig/commands.py")
70
 
        self.assertEqual(mod, "mod/dommands.py")
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);
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)
87
 
 
88
 
    def testValidHunkHeader2(self):
89
 
        """Parse a tricky, valid hunk header"""
90
 
        header = "@@ -1 +0,0 @@\n"
91
 
        hunk = hunk_from_header(header);
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)
97
 
 
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
 
 
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)
122
 
        self.assertIsInstance(line, type)
123
 
        self.assertEqual(str(line), text)
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)
133
 
 
134
 
    def testMalformedLine(self):
135
 
        """Parse invalid valid hunk lines"""
136
 
        self.makeMalformedLine("hello\n")
137
 
 
138
 
    def testMalformedLineNO_NL(self):
139
 
        """Parse invalid '\ No newline at end of file' in hunk lines"""
140
 
        self.makeMalformedLine(NO_NL)
141
 
 
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"""
153
 
        patchtext = self.datafile("patchtext.patch").read()
154
 
        self.compare_parsed(patchtext)
155
 
 
156
 
    def test_parse_binary(self):
157
 
        """Test parsing a whole patch"""
158
 
        patches = parse_patches(self.data_lines("binary.patch"))
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]),
164
 
                                  'Binary files bar\t.* and qux\t.* differ\n')
165
 
 
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
 
 
175
 
    def test_roundtrip_binary(self):
176
 
        patchtext = ''.join(self.data_lines("binary.patch"))
177
 
        patches = parse_patches(patchtext.splitlines(True))
178
 
        self.assertEqual(patchtext, ''.join(str(p) for p in patches))
179
 
 
180
 
    def testInit(self):
181
 
        """Handle patches missing half the position, range tuple"""
182
 
        patchtext = \
183
 
"""--- orig/__vavg__.cl
184
 
+++ mod/__vavg__.cl
185
 
@@ -1 +1,2 @@
186
 
 __qbpsbezng__ = "erfgehpgherqgrkg ra"
187
 
+__qbp__ = Na nygreangr Nepu pbzznaqyvar vagresnpr
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
203
 
            self.assertEqual(mod[mod_pos], orig[i])
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))
212
 
                    self.assertEqual(line.contents, next)
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'),
223
 
            ('diff-7', 'orig-7', 'mod-7'),
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
 
 
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
 
 
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'),
251
 
            ('diff-7', 'orig-7', 'mod-7'),
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
 
 
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"))
269
 
        self.assertEqual(patch.pos_in_mod(0), 1)
270
 
 
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))
305
 
        self.assertEqual(patch_files, filenames)
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())