##// END OF EJS Templates
diffs: handle paths with quotes in diffs.
dan -
r3902:4ef42a9b default
parent child Browse files
Show More
@@ -0,0 +1,7 b''
1 diff --git "a/\"><img src=x onerror=prompt(0)>/\"><img src=x onerror=prompt(1)>.txt" "b/\"><img src=x onerror=prompt(0)>/\"><img src=x onerror=prompt(1)>.txt"
2 index e69de29..8eb97de 100644
3 --- "a/\"><img src=x onerror=prompt(0)>/\"><img src=x onerror=prompt(1)>.txt"
4 +++ "b/\"><img src=x onerror=prompt(0)>/\"><img src=x onerror=prompt(1)>.txt"
5 @@ -0,0 +1 @@
6 +Ehlo eglo
7 \ No newline at end of file
@@ -1,49 +1,49 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 GIT diff module
23 23 """
24 24
25 25 import re
26 26
27 27 from rhodecode.lib.vcs.backends import base
28 28
29 29
30 30 class GitDiff(base.Diff):
31 31
32 32 _header_re = re.compile(r"""
33 33 #^diff[ ]--git
34 34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
35 35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
36 36 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
37 37 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
38 38 (?:^rename[ ]from[ ](?P<rename_from>[^\r\n]+)\n
39 39 ^rename[ ]to[ ](?P<rename_to>[^\r\n]+)(?:\n|$))?
40 40 (?:^copy[ ]from[ ](?P<copy_from>[^\r\n]+)\n
41 41 ^copy[ ]to[ ](?P<copy_to>[^\r\n]+)(?:\n|$))?
42 42 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
43 43 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
44 44 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
45 45 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
46 46 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
47 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
48 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
47 (?:^---[ ]("?a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
48 (?:^\+\+\+[ ]("?b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
49 49 """, re.VERBOSE | re.MULTILINE)
@@ -1,49 +1,49 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 GIT diff module
22 HG diff module
23 23 """
24 24
25 25 import re
26 26
27 27 from rhodecode.lib.vcs.backends import base
28 28
29 29
30 30 class MercurialDiff(base.Diff):
31 31
32 32 _header_re = re.compile(r"""
33 33 #^diff[ ]--git
34 34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
35 35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
36 36 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
37 37 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
38 38 (?:^rename[ ]from[ ](?P<rename_from>[^\r\n]+)\n
39 39 ^rename[ ]to[ ](?P<rename_to>[^\r\n]+)(?:\n|$))?
40 40 (?:^copy[ ]from[ ](?P<copy_from>[^\r\n]+)\n
41 41 ^copy[ ]to[ ](?P<copy_to>[^\r\n]+)(?:\n|$))?
42 42 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
43 43 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
44 44 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
45 45 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
46 46 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
47 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
48 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
47 (?:^---[ ]("?a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
48 (?:^\+\+\+[ ]("?b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
49 49 """, re.VERBOSE | re.MULTILINE)
@@ -1,52 +1,51 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21
22 21 """
23 22 SVN diff module
24 23 """
25 24
26 25 import re
27 26
28 27 from rhodecode.lib.vcs.backends import base
29 28
30 29
31 30 class SubversionDiff(base.Diff):
32 31
33 32 _meta_re = re.compile(r"""
34 33 (?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
35 34 """, re.VERBOSE | re.MULTILINE)
36 35
37 36 _header_re = re.compile(r"""
38 37 #^diff[ ]--git
39 38 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
40 39 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
41 40 ^rename[ ]from[ ](?P<rename_from>[^\r\n]+)\n
42 41 ^rename[ ]to[ ](?P<rename_to>[^\r\n]+)(?:\n|$))?
43 42 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
44 43 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
45 44 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
46 45 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
47 46 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
48 47 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
49 48 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
50 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
51 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
49 (?:^---[ ]("?a/(?P<a_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
50 (?:^\+\+\+[ ]("?b/(?P<b_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
52 51 """, re.VERBOSE | re.MULTILINE)
@@ -1,817 +1,825 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import textwrap
22 22
23 23 import mock
24 24 import pytest
25 25
26 26 from rhodecode.lib.codeblocks import DiffSet
27 27 from rhodecode.lib.diffs import (
28 28 DiffProcessor,
29 29 NEW_FILENODE, DEL_FILENODE, MOD_FILENODE, RENAMED_FILENODE,
30 30 CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE)
31 31 from rhodecode.lib.utils2 import AttributeDict
32 32 from rhodecode.lib.vcs.backends.git import GitCommit
33 33 from rhodecode.tests.fixture import Fixture, no_newline_id_generator
34 34 from rhodecode.lib.vcs.backends.git.repository import GitDiff
35 35 from rhodecode.lib.vcs.backends.hg.repository import MercurialDiff
36 36 from rhodecode.lib.vcs.backends.svn.repository import SubversionDiff
37 37
38 38 fixture = Fixture()
39 39
40 40
41 41 def test_diffprocessor_as_html_with_comments():
42 42 raw_diff = textwrap.dedent('''
43 43 diff --git a/setup.py b/setup.py
44 44 index 5b36422..cfd698e 100755
45 45 --- a/setup.py
46 46 +++ b/setup.py
47 47 @@ -2,7 +2,7 @@
48 48 #!/usr/bin/python
49 49 # Setup file for X
50 50 # Copyright (C) No one
51 51 -
52 52 +x
53 53 try:
54 54 from setuptools import setup, Extension
55 55 except ImportError:
56 56 ''')
57 57 diff = GitDiff(raw_diff)
58 58 processor = DiffProcessor(diff)
59 59 processor.prepare()
60 60
61 61 # Note that the cell with the context in line 5 (in the html) has the
62 62 # no-comment class, which will prevent the add comment icon to be displayed.
63 63 expected_html = textwrap.dedent('''
64 64 <table class="code-difftable">
65 65 <tr class="line context">
66 66 <td class="add-comment-line"><span class="add-comment-content"></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
67 67 <td class="lineno old">...</td>
68 68 <td class="lineno new">...</td>
69 69 <td class="code no-comment">
70 70 <pre>@@ -2,7 +2,7 @@
71 71 </pre>
72 72 </td>
73 73 </tr>
74 74 <tr class="line unmod">
75 75 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
76 76 <td id="setuppy_o2" class="lineno old"><a href="#setuppy_o2" class="tooltip"
77 77 title="Click to select line">2</a></td>
78 78 <td id="setuppy_n2" class="lineno new"><a href="#setuppy_n2" class="tooltip"
79 79 title="Click to select line">2</a></td>
80 80 <td class="code">
81 81 <pre>#!/usr/bin/python
82 82 </pre>
83 83 </td>
84 84 </tr>
85 85 <tr class="line unmod">
86 86 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
87 87 <td id="setuppy_o3" class="lineno old"><a href="#setuppy_o3" class="tooltip"
88 88 title="Click to select line">3</a></td>
89 89 <td id="setuppy_n3" class="lineno new"><a href="#setuppy_n3" class="tooltip"
90 90 title="Click to select line">3</a></td>
91 91 <td class="code">
92 92 <pre># Setup file for X
93 93 </pre>
94 94 </td>
95 95 </tr>
96 96 <tr class="line unmod">
97 97 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
98 98 <td id="setuppy_o4" class="lineno old"><a href="#setuppy_o4" class="tooltip"
99 99 title="Click to select line">4</a></td>
100 100 <td id="setuppy_n4" class="lineno new"><a href="#setuppy_n4" class="tooltip"
101 101 title="Click to select line">4</a></td>
102 102 <td class="code">
103 103 <pre># Copyright (C) No one
104 104 </pre>
105 105 </td>
106 106 </tr>
107 107 <tr class="line del">
108 108 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
109 109 <td id="setuppy_o5" class="lineno old"><a href="#setuppy_o5" class="tooltip"
110 110 title="Click to select line">5</a></td>
111 111 <td class="lineno new"><a href="#setuppy_n" class="tooltip"
112 112 title="Click to select line"></a></td>
113 113 <td class="code">
114 114 <pre>
115 115 </pre>
116 116 </td>
117 117 </tr>
118 118 <tr class="line add">
119 119 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
120 120 <td class="lineno old"><a href="#setuppy_o" class="tooltip"
121 121 title="Click to select line"></a></td>
122 122 <td id="setuppy_n5" class="lineno new"><a href="#setuppy_n5" class="tooltip"
123 123 title="Click to select line">5</a></td>
124 124 <td class="code">
125 125 <pre><ins>x</ins>
126 126 </pre>
127 127 </td>
128 128 </tr>
129 129 <tr class="line unmod">
130 130 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
131 131 <td id="setuppy_o6" class="lineno old"><a href="#setuppy_o6" class="tooltip"
132 132 title="Click to select line">6</a></td>
133 133 <td id="setuppy_n6" class="lineno new"><a href="#setuppy_n6" class="tooltip"
134 134 title="Click to select line">6</a></td>
135 135 <td class="code">
136 136 <pre>try:
137 137 </pre>
138 138 </td>
139 139 </tr>
140 140 <tr class="line unmod">
141 141 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
142 142 <td id="setuppy_o7" class="lineno old"><a href="#setuppy_o7" class="tooltip"
143 143 title="Click to select line">7</a></td>
144 144 <td id="setuppy_n7" class="lineno new"><a href="#setuppy_n7" class="tooltip"
145 145 title="Click to select line">7</a></td>
146 146 <td class="code">
147 147 <pre> from setuptools import setup, Extension
148 148 </pre>
149 149 </td>
150 150 </tr>
151 151 <tr class="line unmod">
152 152 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td><td class="comment-toggle tooltip" title="Toggle Comment Thread"><i class="icon-comment"></i></td>
153 153 <td id="setuppy_o8" class="lineno old"><a href="#setuppy_o8" class="tooltip"
154 154 title="Click to select line">8</a></td>
155 155 <td id="setuppy_n8" class="lineno new"><a href="#setuppy_n8" class="tooltip"
156 156 title="Click to select line">8</a></td>
157 157 <td class="code">
158 158 <pre>except ImportError:
159 159 </pre>
160 160 </td>
161 161 </tr>
162 162 </table>
163 163 ''').strip()
164 164 html = processor.as_html(enable_comments=True).replace('\t', ' ')
165 165
166 166 assert html == expected_html
167 167
168 168
169 169 class TestMixedFilenameEncodings(object):
170 170
171 171 @pytest.fixture(scope="class")
172 172 def raw_diff(self):
173 173 return fixture.load_resource(
174 174 'hg_diff_mixed_filename_encodings.diff')
175 175
176 176 @pytest.fixture
177 177 def processor(self, raw_diff):
178 178 diff = MercurialDiff(raw_diff)
179 179 processor = DiffProcessor(diff)
180 180 return processor
181 181
182 182 def test_filenames_are_decoded_to_unicode(self, processor):
183 183 diff_data = processor.prepare()
184 184 filenames = [item['filename'] for item in diff_data]
185 185 assert filenames == [
186 186 u'spΓ€cial-utf8.txt', u'spοΏ½cial-cp1252.txt', u'spοΏ½cial-latin1.txt']
187 187
188 188 def test_raw_diff_is_decoded_to_unicode(self, processor):
189 189 diff_data = processor.prepare()
190 190 raw_diffs = [item['raw_diff'] for item in diff_data]
191 191 new_file_message = u'\nnew file mode 100644\n'
192 192 expected_raw_diffs = [
193 193 u' a/spΓ€cial-utf8.txt b/spΓ€cial-utf8.txt' + new_file_message,
194 194 u' a/spοΏ½cial-cp1252.txt b/spοΏ½cial-cp1252.txt' + new_file_message,
195 195 u' a/spοΏ½cial-latin1.txt b/spοΏ½cial-latin1.txt' + new_file_message]
196 196 assert raw_diffs == expected_raw_diffs
197 197
198 198 def test_as_raw_preserves_the_encoding(self, processor, raw_diff):
199 199 assert processor.as_raw() == raw_diff
200 200
201 201
202 202 # TODO: mikhail: format the following data structure properly
203 203 DIFF_FIXTURES = [
204 204 ('hg',
205 205 'hg_diff_add_single_binary_file.diff',
206 206 [('US Warszawa.jpg', 'A',
207 207 {'added': 0,
208 208 'deleted': 0,
209 209 'binary': True,
210 210 'ops': {NEW_FILENODE: 'new file 100755',
211 211 BIN_FILENODE: 'binary diff hidden'}}),
212 212 ]),
213 213 ('hg',
214 214 'hg_diff_mod_single_binary_file.diff',
215 215 [('US Warszawa.jpg', 'M',
216 216 {'added': 0,
217 217 'deleted': 0,
218 218 'binary': True,
219 219 'ops': {MOD_FILENODE: 'modified file',
220 220 BIN_FILENODE: 'binary diff hidden'}}),
221 221 ]),
222 222 ('hg',
223 223 'hg_diff_mod_single_file_and_rename_and_chmod.diff',
224 224 [('README', 'M',
225 225 {'added': 3,
226 226 'deleted': 0,
227 227 'binary': False,
228 228 'ops': {MOD_FILENODE: 'modified file',
229 229 RENAMED_FILENODE: 'file renamed from README.rst to README',
230 230 CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
231 231 ]),
232 232 ('hg',
233 233 'hg_diff_no_newline.diff',
234 234 [('server.properties', 'M',
235 235 {'added': 2,
236 236 'deleted': 1,
237 237 'binary': False,
238 238 'ops': {MOD_FILENODE: 'modified file'}}),
239 239 ]),
240 240 ('hg',
241 241 'hg_diff_mod_file_and_rename.diff',
242 242 [('README.rst', 'M',
243 243 {'added': 3,
244 244 'deleted': 0,
245 245 'binary': False,
246 246 'ops': {MOD_FILENODE: 'modified file',
247 247 RENAMED_FILENODE: 'file renamed from README to README.rst'}}),
248 248 ]),
249 249 ('hg',
250 250 'hg_diff_del_single_binary_file.diff',
251 251 [('US Warszawa.jpg', 'D',
252 252 {'added': 0,
253 253 'deleted': 0,
254 254 'binary': True,
255 255 'ops': {DEL_FILENODE: 'deleted file',
256 256 BIN_FILENODE: 'binary diff hidden'}}),
257 257 ]),
258 258 ('hg',
259 259 'hg_diff_chmod_and_mod_single_binary_file.diff',
260 260 [('gravatar.png', 'M',
261 261 {'added': 0,
262 262 'deleted': 0,
263 263 'binary': True,
264 264 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
265 265 BIN_FILENODE: 'binary diff hidden'}}),
266 266 ]),
267 267 ('hg',
268 268 'hg_diff_chmod.diff',
269 269 [('file', 'M',
270 270 {'added': 0,
271 271 'deleted': 0,
272 272 'binary': True,
273 273 'ops': {CHMOD_FILENODE: 'modified file chmod 100755 => 100644'}}),
274 274 ]),
275 275 ('hg',
276 276 'hg_diff_rename_file.diff',
277 277 [('file_renamed', 'M',
278 278 {'added': 0,
279 279 'deleted': 0,
280 280 'binary': True,
281 281 'ops': {RENAMED_FILENODE: 'file renamed from file to file_renamed'}}),
282 282 ]),
283 283 ('hg',
284 284 'hg_diff_rename_and_chmod_file.diff',
285 285 [('README', 'M',
286 286 {'added': 0,
287 287 'deleted': 0,
288 288 'binary': True,
289 289 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755',
290 290 RENAMED_FILENODE: 'file renamed from README.rst to README'}}),
291 291 ]),
292 292 ('hg',
293 293 'hg_diff_binary_and_normal.diff',
294 294 [('img/baseline-10px.png', 'A',
295 295 {'added': 0,
296 296 'deleted': 0,
297 297 'binary': True,
298 298 'ops': {NEW_FILENODE: 'new file 100644',
299 299 BIN_FILENODE: 'binary diff hidden'}}),
300 300 ('js/jquery/hashgrid.js', 'A',
301 301 {'added': 340,
302 302 'deleted': 0,
303 303 'binary': False,
304 304 'ops': {NEW_FILENODE: 'new file 100755'}}),
305 305 ('index.html', 'M',
306 306 {'added': 3,
307 307 'deleted': 2,
308 308 'binary': False,
309 309 'ops': {MOD_FILENODE: 'modified file'}}),
310 310 ('less/docs.less', 'M',
311 311 {'added': 34,
312 312 'deleted': 0,
313 313 'binary': False,
314 314 'ops': {MOD_FILENODE: 'modified file'}}),
315 315 ('less/scaffolding.less', 'M',
316 316 {'added': 1,
317 317 'deleted': 3,
318 318 'binary': False,
319 319 'ops': {MOD_FILENODE: 'modified file'}}),
320 320 ('readme.markdown', 'M',
321 321 {'added': 1,
322 322 'deleted': 10,
323 323 'binary': False,
324 324 'ops': {MOD_FILENODE: 'modified file'}}),
325 325 ('img/baseline-20px.png', 'D',
326 326 {'added': 0,
327 327 'deleted': 0,
328 328 'binary': True,
329 329 'ops': {DEL_FILENODE: 'deleted file',
330 330 BIN_FILENODE: 'binary diff hidden'}}),
331 331 ('js/global.js', 'D',
332 332 {'added': 0,
333 333 'deleted': 75,
334 334 'binary': False,
335 335 'ops': {DEL_FILENODE: 'deleted file'}})
336 336 ]),
337 337 ('git',
338 338 'git_diff_chmod.diff',
339 339 [('work-horus.xls', 'M',
340 340 {'added': 0,
341 341 'deleted': 0,
342 342 'binary': True,
343 343 'ops': {CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}})
344 344 ]),
345 345 ('git',
346 'git_diff_js_chars.diff',
347 [('\\"><img src=x onerror=prompt(0)>/\\"><img src=x onerror=prompt(1)>.txt', 'M',
348 {'added': 1,
349 'deleted': 0,
350 'binary': False,
351 'ops': {MOD_FILENODE: 'modified file'}})
352 ]),
353 ('git',
346 354 'git_diff_rename_file.diff',
347 355 [('file.xls', 'M',
348 356 {'added': 0,
349 357 'deleted': 0,
350 358 'binary': True,
351 359 'ops': {
352 360 RENAMED_FILENODE: 'file renamed from work-horus.xls to file.xls'}})
353 361 ]),
354 362 ('git',
355 363 'git_diff_mod_single_binary_file.diff',
356 364 [('US Warszawa.jpg', 'M',
357 365 {'added': 0,
358 366 'deleted': 0,
359 367 'binary': True,
360 368 'ops': {MOD_FILENODE: 'modified file',
361 369 BIN_FILENODE: 'binary diff hidden'}})
362 370 ]),
363 371 ('git',
364 372 'git_diff_binary_and_normal.diff',
365 373 [('img/baseline-10px.png', 'A',
366 374 {'added': 0,
367 375 'deleted': 0,
368 376 'binary': True,
369 377 'ops': {NEW_FILENODE: 'new file 100644',
370 378 BIN_FILENODE: 'binary diff hidden'}}),
371 379 ('js/jquery/hashgrid.js', 'A',
372 380 {'added': 340,
373 381 'deleted': 0,
374 382 'binary': False,
375 383 'ops': {NEW_FILENODE: 'new file 100755'}}),
376 384 ('index.html', 'M',
377 385 {'added': 3,
378 386 'deleted': 2,
379 387 'binary': False,
380 388 'ops': {MOD_FILENODE: 'modified file'}}),
381 389 ('less/docs.less', 'M',
382 390 {'added': 34,
383 391 'deleted': 0,
384 392 'binary': False,
385 393 'ops': {MOD_FILENODE: 'modified file'}}),
386 394 ('less/scaffolding.less', 'M',
387 395 {'added': 1,
388 396 'deleted': 3,
389 397 'binary': False,
390 398 'ops': {MOD_FILENODE: 'modified file'}}),
391 399 ('readme.markdown', 'M',
392 400 {'added': 1,
393 401 'deleted': 10,
394 402 'binary': False,
395 403 'ops': {MOD_FILENODE: 'modified file'}}),
396 404 ('img/baseline-20px.png', 'D',
397 405 {'added': 0,
398 406 'deleted': 0,
399 407 'binary': True,
400 408 'ops': {DEL_FILENODE: 'deleted file',
401 409 BIN_FILENODE: 'binary diff hidden'}}),
402 410 ('js/global.js', 'D',
403 411 {'added': 0,
404 412 'deleted': 75,
405 413 'binary': False,
406 414 'ops': {DEL_FILENODE: 'deleted file'}}),
407 415 ]),
408 416 ('hg',
409 417 'diff_with_diff_data.diff',
410 418 [('vcs/backends/base.py', 'M',
411 419 {'added': 18,
412 420 'deleted': 2,
413 421 'binary': False,
414 422 'ops': {MOD_FILENODE: 'modified file'}}),
415 423 ('vcs/backends/git/repository.py', 'M',
416 424 {'added': 46,
417 425 'deleted': 15,
418 426 'binary': False,
419 427 'ops': {MOD_FILENODE: 'modified file'}}),
420 428 ('vcs/backends/hg.py', 'M',
421 429 {'added': 22,
422 430 'deleted': 3,
423 431 'binary': False,
424 432 'ops': {MOD_FILENODE: 'modified file'}}),
425 433 ('vcs/tests/test_git.py', 'M',
426 434 {'added': 5,
427 435 'deleted': 5,
428 436 'binary': False,
429 437 'ops': {MOD_FILENODE: 'modified file'}}),
430 438 ('vcs/tests/test_repository.py', 'M',
431 439 {'added': 174,
432 440 'deleted': 2,
433 441 'binary': False,
434 442 'ops': {MOD_FILENODE: 'modified file'}}),
435 443 ]),
436 444 ('hg',
437 445 'hg_diff_copy_file.diff',
438 446 [('file2', 'M',
439 447 {'added': 0,
440 448 'deleted': 0,
441 449 'binary': True,
442 450 'ops': {COPIED_FILENODE: 'file copied from file1 to file2'}}),
443 451 ]),
444 452 ('hg',
445 453 'hg_diff_copy_and_modify_file.diff',
446 454 [('file3', 'M',
447 455 {'added': 1,
448 456 'deleted': 0,
449 457 'binary': False,
450 458 'ops': {COPIED_FILENODE: 'file copied from file2 to file3',
451 459 MOD_FILENODE: 'modified file'}}),
452 460 ]),
453 461 ('hg',
454 462 'hg_diff_copy_and_chmod_file.diff',
455 463 [('file4', 'M',
456 464 {'added': 0,
457 465 'deleted': 0,
458 466 'binary': True,
459 467 'ops': {COPIED_FILENODE: 'file copied from file3 to file4',
460 468 CHMOD_FILENODE: 'modified file chmod 100644 => 100755'}}),
461 469 ]),
462 470 ('hg',
463 471 'hg_diff_copy_chmod_and_edit_file.diff',
464 472 [('file5', 'M',
465 473 {'added': 2,
466 474 'deleted': 1,
467 475 'binary': False,
468 476 'ops': {COPIED_FILENODE: 'file copied from file4 to file5',
469 477 CHMOD_FILENODE: 'modified file chmod 100755 => 100644',
470 478 MOD_FILENODE: 'modified file'}})]),
471 479
472 480 # Diffs to validate rename and copy file with space in its name
473 481 ('git',
474 482 'git_diff_rename_file_with_spaces.diff',
475 483 [('file_with_ two spaces.txt', 'M',
476 484 {'added': 0,
477 485 'deleted': 0,
478 486 'binary': True,
479 487 'ops': {
480 488 RENAMED_FILENODE: (
481 489 'file renamed from file_with_ spaces.txt to file_with_ '
482 490 ' two spaces.txt')}
483 491 }), ]),
484 492 ('hg',
485 493 'hg_diff_rename_file_with_spaces.diff',
486 494 [('file_changed _.txt', 'M',
487 495 {'added': 0,
488 496 'deleted': 0,
489 497 'binary': True,
490 498 'ops': {
491 499 RENAMED_FILENODE: (
492 500 'file renamed from file_ with update.txt to file_changed'
493 501 ' _.txt')}
494 502 }), ]),
495 503 ('hg',
496 504 'hg_diff_copy_file_with_spaces.diff',
497 505 [('file_copied_ with spaces.txt', 'M',
498 506 {'added': 0,
499 507 'deleted': 0,
500 508 'binary': True,
501 509 'ops': {
502 510 COPIED_FILENODE: (
503 511 'file copied from file_changed_without_spaces.txt to'
504 512 ' file_copied_ with spaces.txt')}
505 513 }),
506 514 ]),
507 515
508 516 # special signs from git
509 517 ('git',
510 518 'git_diff_binary_special_files.diff',
511 519 [('css/_Icon\\r', 'A',
512 520 {'added': 0,
513 521 'deleted': 0,
514 522 'binary': True,
515 523 'ops': {NEW_FILENODE: 'new file 100644',
516 524 BIN_FILENODE: 'binary diff hidden'}
517 525 }),
518 526 ]),
519 527 ('git',
520 528 'git_diff_binary_special_files_2.diff',
521 529 [('css/Icon\\r', 'A',
522 530 {'added': 0,
523 531 'deleted': 0,
524 532 'binary': True,
525 533 'ops': {NEW_FILENODE: 'new file 100644', }
526 534 }),
527 535 ]),
528 536
529 537 ('svn',
530 538 'svn_diff_binary_add_file.diff',
531 539 [('intl.dll', 'A',
532 540 {'added': 0,
533 541 'deleted': 0,
534 542 'binary': False,
535 543 'ops': {NEW_FILENODE: 'new file 10644',
536 544 #TODO(Marcink): depends on binary detection on svn patches
537 545 # BIN_FILENODE: 'binary diff hidden'
538 546 }
539 547 }),
540 548 ]),
541 549
542 550 ('svn',
543 551 'svn_diff_multiple_changes.diff',
544 552 [('trunk/doc/images/SettingsOverlay.png', 'M',
545 553 {'added': 0,
546 554 'deleted': 0,
547 555 'binary': False,
548 556 'ops': {MOD_FILENODE: 'modified file',
549 557 #TODO(Marcink): depends on binary detection on svn patches
550 558 # BIN_FILENODE: 'binary diff hidden'
551 559 }
552 560 }),
553 561 ('trunk/doc/source/de/tsvn_ch04.xml', 'M',
554 562 {'added': 89,
555 563 'deleted': 34,
556 564 'binary': False,
557 565 'ops': {MOD_FILENODE: 'modified file'}
558 566 }),
559 567 ('trunk/doc/source/en/tsvn_ch04.xml', 'M',
560 568 {'added': 66,
561 569 'deleted': 21,
562 570 'binary': False,
563 571 'ops': {MOD_FILENODE: 'modified file'}
564 572 }),
565 573 ('trunk/src/Changelog.txt', 'M',
566 574 {'added': 2,
567 575 'deleted': 0,
568 576 'binary': False,
569 577 'ops': {MOD_FILENODE: 'modified file'}
570 578 }),
571 579 ('trunk/src/Resources/TortoiseProcENG.rc', 'M',
572 580 {'added': 19,
573 581 'deleted': 13,
574 582 'binary': False,
575 583 'ops': {MOD_FILENODE: 'modified file'}
576 584 }),
577 585 ('trunk/src/TortoiseProc/SetOverlayPage.cpp', 'M',
578 586 {'added': 16,
579 587 'deleted': 1,
580 588 'binary': False,
581 589 'ops': {MOD_FILENODE: 'modified file'}
582 590 }),
583 591 ('trunk/src/TortoiseProc/SetOverlayPage.h', 'M',
584 592 {'added': 3,
585 593 'deleted': 0,
586 594 'binary': False,
587 595 'ops': {MOD_FILENODE: 'modified file'}
588 596 }),
589 597 ('trunk/src/TortoiseProc/resource.h', 'M',
590 598 {'added': 2,
591 599 'deleted': 0,
592 600 'binary': False,
593 601 'ops': {MOD_FILENODE: 'modified file'}
594 602 }),
595 603 ('trunk/src/TortoiseShell/ShellCache.h', 'M',
596 604 {'added': 50,
597 605 'deleted': 1,
598 606 'binary': False,
599 607 'ops': {MOD_FILENODE: 'modified file'}
600 608 }),
601 609 ]),
602 610
603 611 ]
604 612
605 613 DIFF_FIXTURES_WITH_CONTENT = [
606 614 (
607 615 'hg', 'hg_diff_single_file_change_newline.diff',
608 616 [
609 617 (
610 618 'file_b', # filename
611 619 'A', # change
612 620 { # stats
613 621 'added': 1,
614 622 'deleted': 0,
615 623 'binary': False,
616 624 'ops': {NEW_FILENODE: 'new file 100644', }
617 625 },
618 626 '@@ -0,0 +1 @@\n+test_content b\n' # diff
619 627 ),
620 628 ],
621 629 ),
622 630 (
623 631 'hg', 'hg_diff_double_file_change_newline.diff',
624 632 [
625 633 (
626 634 'file_b', # filename
627 635 'A', # change
628 636 { # stats
629 637 'added': 1,
630 638 'deleted': 0,
631 639 'binary': False,
632 640 'ops': {NEW_FILENODE: 'new file 100644', }
633 641 },
634 642 '@@ -0,0 +1 @@\n+test_content b\n' # diff
635 643 ),
636 644 (
637 645 'file_c', # filename
638 646 'A', # change
639 647 { # stats
640 648 'added': 1,
641 649 'deleted': 0,
642 650 'binary': False,
643 651 'ops': {NEW_FILENODE: 'new file 100644', }
644 652 },
645 653 '@@ -0,0 +1 @@\n+test_content c\n' # diff
646 654 ),
647 655 ],
648 656 ),
649 657 (
650 658 'hg', 'hg_diff_double_file_change_double_newline.diff',
651 659 [
652 660 (
653 661 'file_b', # filename
654 662 'A', # change
655 663 { # stats
656 664 'added': 1,
657 665 'deleted': 0,
658 666 'binary': False,
659 667 'ops': {NEW_FILENODE: 'new file 100644', }
660 668 },
661 669 '@@ -0,0 +1 @@\n+test_content b\n\n' # diff
662 670 ),
663 671 (
664 672 'file_c', # filename
665 673 'A', # change
666 674 { # stats
667 675 'added': 1,
668 676 'deleted': 0,
669 677 'binary': False,
670 678 'ops': {NEW_FILENODE: 'new file 100644', }
671 679 },
672 680 '@@ -0,0 +1 @@\n+test_content c\n' # diff
673 681 ),
674 682 ],
675 683 ),
676 684 (
677 685 'hg', 'hg_diff_four_file_change_newline.diff',
678 686 [
679 687 (
680 688 'file', # filename
681 689 'A', # change
682 690 { # stats
683 691 'added': 1,
684 692 'deleted': 0,
685 693 'binary': False,
686 694 'ops': {NEW_FILENODE: 'new file 100644', }
687 695 },
688 696 '@@ -0,0 +1,1 @@\n+file\n' # diff
689 697 ),
690 698 (
691 699 'file2', # filename
692 700 'A', # change
693 701 { # stats
694 702 'added': 1,
695 703 'deleted': 0,
696 704 'binary': False,
697 705 'ops': {NEW_FILENODE: 'new file 100644', }
698 706 },
699 707 '@@ -0,0 +1,1 @@\n+another line\n' # diff
700 708 ),
701 709 (
702 710 'file3', # filename
703 711 'A', # change
704 712 { # stats
705 713 'added': 1,
706 714 'deleted': 0,
707 715 'binary': False,
708 716 'ops': {NEW_FILENODE: 'new file 100644', }
709 717 },
710 718 '@@ -0,0 +1,1 @@\n+newline\n' # diff
711 719 ),
712 720 (
713 721 'file4', # filename
714 722 'A', # change
715 723 { # stats
716 724 'added': 1,
717 725 'deleted': 0,
718 726 'binary': False,
719 727 'ops': {NEW_FILENODE: 'new file 100644', }
720 728 },
721 729 '@@ -0,0 +1,1 @@\n+fil4\n\\ No newline at end of file' # diff
722 730 ),
723 731 ],
724 732 ),
725 733
726 734 ]
727 735
728 736
729 737 diff_class = {
730 738 'git': GitDiff,
731 739 'hg': MercurialDiff,
732 740 'svn': SubversionDiff,
733 741 }
734 742
735 743
736 744 @pytest.fixture(params=DIFF_FIXTURES)
737 745 def diff_fixture(request):
738 746 vcs, diff_fixture, expected = request.param
739 747 diff_txt = fixture.load_resource(diff_fixture)
740 748 diff = diff_class[vcs](diff_txt)
741 749 return diff, expected
742 750
743 751
744 752 def test_diff_lib(diff_fixture):
745 753 diff, expected_data = diff_fixture
746 754 diff_proc = DiffProcessor(diff)
747 755 diff_proc_d = diff_proc.prepare()
748 756 data = [(x['filename'], x['operation'], x['stats']) for x in diff_proc_d]
749 757 assert expected_data == data
750 758
751 759
752 760 @pytest.fixture(params=DIFF_FIXTURES_WITH_CONTENT)
753 761 def diff_fixture_w_content(request):
754 762 vcs, diff_fixture, expected = request.param
755 763 diff_txt = fixture.load_resource(diff_fixture)
756 764 diff = diff_class[vcs](diff_txt)
757 765 return diff, expected
758 766
759 767
760 768 def test_diff_over_limit(request):
761 769
762 770 diff_limit = 1024
763 771 file_limit = 1024
764 772
765 773 raw_diff = fixture.load_resource('large_diff.diff')
766 774 vcs_diff = GitDiff(raw_diff)
767 775 diff_processor = DiffProcessor(
768 776 vcs_diff, format='newdiff', diff_limit=diff_limit, file_limit=file_limit,
769 777 show_full_diff=False)
770 778
771 779 _parsed = diff_processor.prepare()
772 780
773 781 commit1 = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
774 782 commit2 = GitCommit(repository=mock.Mock(), raw_id='abcdef34', idx=2)
775 783
776 784 diffset = DiffSet(
777 785 repo_name='repo_name',
778 786 source_node_getter=lambda *a, **kw: AttributeDict({'commit': commit1}),
779 787 target_node_getter=lambda *a, **kw: AttributeDict({'commit': commit2})
780 788 )
781 789
782 790 diffset = diffset.render_patchset(_parsed, commit1, commit2)
783 791
784 792 assert len(diffset.files) == 2
785 793 assert diffset.limited_diff is True
786 794 assert diffset.files[0].patch['filename'] == 'example.go'
787 795 assert diffset.files[0].limited_diff is True
788 796
789 797 assert diffset.files[1].patch['filename'] == 'README.md'
790 798 assert diffset.files[1].limited_diff is False
791 799
792 800
793 801 def test_diff_lib_newlines(diff_fixture_w_content):
794 802 diff, expected_data = diff_fixture_w_content
795 803 diff_proc = DiffProcessor(diff)
796 804 diff_proc_d = diff_proc.prepare()
797 805 data = [(x['filename'], x['operation'], x['stats'], x['raw_diff'])
798 806 for x in diff_proc_d]
799 807 assert expected_data == data
800 808
801 809
802 810 @pytest.mark.parametrize('input_str', [
803 811 '',
804 812 '\n',
805 813 '\n\n',
806 814 'First\n+second',
807 815 'First\n+second\n',
808 816
809 817 '\n\n\n Multi \n\n\n',
810 818 '\n\n\n Multi beginning',
811 819 'Multi end \n\n\n',
812 820 'Multi end',
813 821 '@@ -0,0 +1 @@\n+test_content \n\n b\n'
814 822 ], ids=no_newline_id_generator)
815 823 def test_splitlines(input_str):
816 824 result = DiffProcessor.diff_splitter(input_str)
817 825 assert list(result) == input_str.splitlines(True)
General Comments 0
You need to be logged in to leave comments. Login now