3 # This file is part of GCC.
5 # GCC is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation; either version 3, or (at your option) any later
10 # GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with GCC; see the file COPYING3. If not see
17 # <http://www.gnu.org/licenses/>. */
23 from git_commit
import GitCommit
25 from git_email
import GitEmail
29 script_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
31 unidiff_supports_renaming
= hasattr(unidiff
.PatchedFile(), 'is_rename')
35 M gcc/ada/impunit.adb'
36 R097 gcc/ada/libgnat/s-atopar.adb gcc/ada/libgnat/s-aoinar.adb
40 class TestGccChangelog(unittest
.TestCase
):
47 with
open(os
.path
.join(script_path
, 'test_patches.txt')) as f
:
49 for line
in lines
.split('\n'):
50 if line
.startswith('==='):
52 self
.patches
[filename
] = patch_lines
53 filename
= line
.split(' ')[1]
56 patch_lines
.append(line
)
58 self
.patches
[filename
] = patch_lines
62 assert t
.endswith('.patch')
65 def get_git_email(self
, filename
):
66 with tempfile
.NamedTemporaryFile(mode
='w+', suffix
='.patch',
68 f
.write('\n'.join(self
.patches
[filename
]))
69 self
.temps
.append(f
.name
)
70 return GitEmail(f
.name
)
72 def from_patch_glob(self
, name
):
73 files
= [f
for f
in self
.patches
.keys() if f
.startswith(name
)]
74 assert len(files
) == 1
75 return self
.get_git_email(files
[0])
77 def test_simple_patch_format(self
):
78 email
= self
.get_git_email('0577-aarch64-Add-an-and.patch')
79 assert not email
.errors
80 assert len(email
.changelog_entries
) == 2
81 entry
= email
.changelog_entries
[0]
82 assert (entry
.author_lines
==
83 [('Richard Sandiford <richard.sandiford@arm.com>',
85 assert len(entry
.authors
) == 1
86 assert (entry
.authors
[0]
87 == 'Richard Sandiford <richard.sandiford@arm.com>')
88 assert entry
.folder
== 'gcc'
89 assert entry
.prs
== ['PR target/87763']
90 assert len(entry
.files
) == 3
91 assert entry
.files
[0] == 'config/aarch64/aarch64-protos.h'
93 def test_daily_bump(self
):
94 email
= self
.get_git_email('0085-Daily-bump.patch')
95 assert not email
.errors
96 assert not email
.changelog_entries
98 def test_deduce_changelog_entries(self
):
99 email
= self
.from_patch_glob('0040')
100 assert len(email
.changelog_entries
) == 2
101 assert email
.changelog_entries
[0].folder
== 'gcc/cp'
102 assert email
.changelog_entries
[0].prs
== ['PR c++/90916']
103 assert email
.changelog_entries
[0].files
== ['pt.c']
104 # this one is added automatically
105 assert email
.changelog_entries
[1].folder
== 'gcc/testsuite'
107 def test_only_changelog_updated(self
):
108 email
= self
.from_patch_glob('0129')
109 assert not email
.errors
110 assert not email
.changelog_entries
112 def test_wrong_mentioned_filename(self
):
113 email
= self
.from_patch_glob('0096')
115 err
= email
.errors
[0]
116 assert err
.message
== 'unchanged file mentioned in a ChangeLog (did ' \
117 'you mean "gcc/testsuite/gcc.target/aarch64/' \
118 'advsimd-intrinsics/vdot-3-1.c"?)'
119 assert err
.line
== 'gcc/testsuite/gcc.target/aarch64/' \
120 'advsimd-intrinsics/vdot-compile-3-1.c'
122 def test_missing_tab(self
):
123 email
= self
.from_patch_glob('0031')
124 assert len(email
.errors
) == 2
125 err
= email
.errors
[0]
126 assert err
.message
== 'line should start with a tab'
127 assert err
.line
== ' * cfgloopanal.c (average_num_loop_insns): ' \
128 'Free bbs when early'
130 def test_leading_changelog_format(self
):
131 email
= self
.from_patch_glob('0184')
132 assert len(email
.errors
) == 4
133 assert email
.errors
[0].line
== 'gcc/c-family/c-cppbuiltins.c'
134 assert email
.errors
[2].line
== 'gcc/c-family/c-cppbuiltin.c'
136 def test_cannot_deduce_no_blank_line(self
):
137 email
= self
.from_patch_glob('0334')
138 assert len(email
.errors
) == 1
139 assert len(email
.changelog_entries
) == 1
140 assert email
.changelog_entries
[0].folder
is None
142 def test_author_lines(self
):
143 email
= self
.from_patch_glob('0814')
144 assert not email
.errors
145 assert (email
.changelog_entries
[0].author_lines
==
146 [('Martin Jambor <mjambor@suse.cz>', '2020-02-19')])
148 def test_multiple_authors_and_prs(self
):
149 email
= self
.from_patch_glob('0735')
150 assert len(email
.changelog_entries
) == 1
151 entry
= email
.changelog_entries
[0]
152 assert len(entry
.author_lines
) == 2
153 assert len(entry
.authors
) == 2
154 assert (entry
.author_lines
[1] ==
155 ('Bernd Edlinger <bernd.edlinger@hotmail.de>', None))
157 def test_multiple_prs(self
):
158 email
= self
.from_patch_glob('1699')
159 assert len(email
.changelog_entries
) == 2
160 assert len(email
.changelog_entries
[0].prs
) == 2
162 def test_missing_PR_component(self
):
163 email
= self
.from_patch_glob('0735')
164 assert len(email
.errors
) == 1
165 assert email
.errors
[0].message
== 'missing PR component'
167 def test_invalid_PR_component(self
):
168 email
= self
.from_patch_glob('0198')
169 assert len(email
.errors
) == 1
170 assert email
.errors
[0].message
== 'invalid PR component'
172 def test_additional_author_list(self
):
173 email
= self
.from_patch_glob('0342')
174 msg
= 'additional author must be indented ' \
175 'with one tab and four spaces'
176 assert email
.errors
[1].message
== msg
178 def test_trailing_whitespaces(self
):
179 email
= self
.get_git_email('trailing-whitespaces.patch')
180 assert len(email
.errors
) == 3
182 def test_space_after_asterisk(self
):
183 email
= self
.from_patch_glob('1999')
184 assert len(email
.errors
) == 1
185 assert email
.errors
[0].message
== 'one space should follow asterisk'
187 def test_long_lines(self
):
188 email
= self
.get_git_email('long-lines.patch')
189 assert len(email
.errors
) == 1
190 assert email
.errors
[0].message
== 'line exceeds 100 character limit'
192 def test_new_files(self
):
193 email
= self
.from_patch_glob('0030')
194 assert not email
.errors
196 def test_wrong_changelog_location(self
):
197 email
= self
.from_patch_glob('0043')
198 assert len(email
.errors
) == 2
199 assert (email
.errors
[0].message
==
200 'wrong ChangeLog location "gcc", should be "gcc/testsuite"')
202 def test_single_author_name(self
):
203 email
= self
.from_patch_glob('1975')
204 assert len(email
.changelog_entries
) == 2
205 assert len(email
.changelog_entries
[0].author_lines
) == 1
206 assert len(email
.changelog_entries
[1].author_lines
) == 1
208 def test_bad_first_line(self
):
209 email
= self
.from_patch_glob('0413')
210 assert len(email
.errors
) == 1
212 def test_co_authored_by(self
):
213 email
= self
.from_patch_glob('1850')
214 assert email
.co_authors
== ['Jakub Jelinek <jakub@redhat.com>']
215 output_entries
= list(email
.to_changelog_entries())
216 assert len(output_entries
) == 2
217 ent0
= output_entries
[0]
218 assert ent0
[1].startswith('2020-04-16 Martin Liska '
219 '<mliska@suse.cz>\n\t'
220 ' Jakub Jelinek <jakub@redhat.com>')
222 def test_multiple_co_author_formats(self
):
223 email
= self
.get_git_email('co-authored-by.patch')
224 assert len(email
.co_authors
) == 3
225 assert email
.co_authors
[0] == 'Jakub Jelinek <jakub@redhat.com>'
226 assert email
.co_authors
[1] == 'John Miller <jm@example.com>'
227 assert email
.co_authors
[2] == 'John Miller2 <jm2@example.com>'
229 def test_new_file_added_entry(self
):
230 email
= self
.from_patch_glob('1957')
231 output_entries
= list(email
.to_changelog_entries())
232 assert len(output_entries
) == 2
233 needle
= ('\t* g++.dg/cpp2a/lambda-generic-variadic20.C'
235 assert output_entries
[1][1].endswith(needle
)
236 assert email
.changelog_entries
[1].prs
== ['PR c++/94546']
238 def test_global_pr_entry(self
):
239 email
= self
.from_patch_glob('2004')
240 assert not email
.errors
241 assert email
.changelog_entries
[0].prs
== ['PR other/94629']
243 def test_unique_prs(self
):
244 email
= self
.get_git_email('pr-check1.patch')
245 assert not email
.errors
246 assert email
.changelog_entries
[0].prs
== ['PR ipa/12345']
247 assert email
.changelog_entries
[1].prs
== []
249 def test_multiple_prs_not_added(self
):
250 email
= self
.from_patch_glob('0002-Add-patch_are')
251 assert not email
.errors
252 assert email
.changelog_entries
[0].prs
== ['PR target/93492']
253 assert email
.changelog_entries
[1].prs
== ['PR target/12345']
254 assert email
.changelog_entries
[2].prs
== []
255 assert email
.changelog_entries
[2].folder
== 'gcc/testsuite'
257 def test_strict_mode(self
):
258 email
= self
.from_patch_glob('0001-Add-patch_are')
259 msg
= 'ChangeLog, DATESTAMP, BASE-VER and DEV-PHASE updates should ' \
260 'be done separately from normal commits'
261 assert email
.errors
[0].message
.startswith(msg
)
263 def test_strict_mode_normal_patch(self
):
264 email
= self
.get_git_email('0001-Just-test-it.patch')
265 assert not email
.errors
267 def test_strict_mode_datestamp_only(self
):
268 email
= self
.get_git_email('0002-Bump-date.patch')
269 assert not email
.errors
271 def test_wrong_changelog_entry(self
):
272 email
= self
.from_patch_glob('0020-IPA-Avoid')
273 msg
= 'first line should start with a tab, an asterisk and a space'
274 assert (email
.errors
[0].message
== msg
)
276 def test_cherry_pick_format(self
):
277 email
= self
.from_patch_glob('0001-c-Alias.patch')
278 assert not email
.errors
280 def test_signatures(self
):
281 email
= self
.from_patch_glob('0001-RISC-V-Make-unique.patch')
282 assert not email
.errors
283 assert len(email
.changelog_entries
) == 1
285 def test_duplicate_top_level_author(self
):
286 email
= self
.from_patch_glob('0001-Fortran-ProcPtr-function.patch')
287 assert not email
.errors
288 assert len(email
.changelog_entries
[0].author_lines
) == 1
290 def test_dr_entry(self
):
291 email
= self
.from_patch_glob('0001-c-C-20-DR-2237.patch')
292 assert email
.changelog_entries
[0].prs
== ['DR 2237']
294 def test_changes_only_in_ignored_location(self
):
295 email
= self
.from_patch_glob('0001-go-in-ignored-location.patch')
296 assert not email
.errors
298 def test_changelog_for_ignored_location(self
):
299 email
= self
.from_patch_glob('0001-Update-merge.sh-to-reflect.patch')
300 assert (email
.changelog_entries
[0].lines
[0]
301 == '\t* LOCAL_PATCHES: Use git hash instead of SVN id.')
303 def test_multiline_file_list(self
):
304 email
= self
.from_patch_glob(
305 '0001-Ada-Reuse-Is_Package_Or_Generic_Package-where-possib.patch')
306 assert (email
.changelog_entries
[0].files
307 == ['contracts.adb', 'einfo.adb', 'exp_ch9.adb',
308 'sem_ch12.adb', 'sem_ch4.adb', 'sem_ch7.adb',
309 'sem_ch8.adb', 'sem_elab.adb', 'sem_type.adb',
312 @unittest.skipIf(not unidiff_supports_renaming
,
313 'Newer version of unidiff is needed (0.6.0+)')
314 def test_renamed_file(self
):
315 email
= self
.from_patch_glob(
316 '0001-Ada-Add-support-for-XDR-streaming-in-the-default-run.patch')
317 assert not email
.errors
319 def test_duplicite_author_lines(self
):
320 email
= self
.from_patch_glob('0001-Fortran-type-is-real-kind-1.patch')
321 assert (email
.changelog_entries
[0].author_lines
[0][0]
322 == 'Steven G. Kargl <kargl@gcc.gnu.org>')
323 assert (email
.changelog_entries
[0].author_lines
[1][0]
324 == 'Mark Eggleston <markeggleston@gcc.gnu.org>')
326 def test_missing_change_description(self
):
327 email
= self
.from_patch_glob('0001-Missing-change-description.patch')
328 assert len(email
.errors
) == 2
329 assert email
.errors
[0].message
== 'missing description of a change'
330 assert email
.errors
[1].message
== 'missing description of a change'
332 def test_libstdcxx_html_regenerated(self
):
333 email
= self
.from_patch_glob('0001-Fix-text-of-hyperlink')
334 assert not email
.errors
335 email
= self
.from_patch_glob('0002-libstdc-Fake-test-change-1.patch')
336 assert len(email
.errors
) == 1
337 msg
= "pattern doesn't match any changed files"
338 assert email
.errors
[0].message
== msg
339 assert email
.errors
[0].line
== 'libstdc++-v3/doc/html/'
340 email
= self
.from_patch_glob('0003-libstdc-Fake-test-change-2.patch')
341 assert len(email
.errors
) == 1
342 msg
= 'changed file not mentioned in a ChangeLog'
343 assert email
.errors
[0].message
== msg
345 def test_not_deduce(self
):
346 email
= self
.from_patch_glob('0001-configure.patch')
347 assert not email
.errors
348 assert len(email
.changelog_entries
) == 2
350 def test_parse_git_name_status(self
):
351 modified_files
= GitCommit
.parse_git_name_status(NAME_STATUS1
)
352 assert len(modified_files
) == 3
353 assert modified_files
[1] == ('gcc/ada/libgnat/s-atopar.adb', 'D')
354 assert modified_files
[2] == ('gcc/ada/libgnat/s-aoinar.adb', 'A')
356 def test_backport(self
):
357 email
= self
.from_patch_glob('0001-asan-fix-RTX-emission.patch')
358 assert not email
.errors
359 expected_hash
= '8cff672cb9a132d3d3158c2edfc9a64b55292b80'
360 assert email
.cherry_pick_commit
== expected_hash
361 assert len(email
.changelog_entries
) == 1
362 entry
= list(email
.to_changelog_entries())[0][1]
363 assert entry
.startswith('2020-06-11 Martin Liska <mliska@suse.cz>')
364 assert '\tBackported from master:' in entry
365 assert '\t2020-06-11 Martin Liska <mliska@suse.cz>' in entry
366 assert '\t\t Jakub Jelinek <jakub@redhat.com>' in entry
368 def test_backport_double_cherry_pick(self
):
369 email
= self
.from_patch_glob('double-cherry-pick.patch')
370 assert email
.errors
[0].message
.startswith('multiple cherry pick lines')
372 def test_square_and_lt_gt(self
):
373 email
= self
.from_patch_glob('0001-Check-for-more-missing')
374 assert not email
.errors
376 def test_empty_parenthesis(self
):
377 email
= self
.from_patch_glob('0001-tree-optimization-97633-fix')
378 assert len(email
.errors
) == 1
379 assert email
.errors
[0].message
== 'empty group "()" found'
381 def test_emptry_entry_desc(self
):
382 email
= self
.from_patch_glob('0001-c-Set-CALL_FROM_NEW_OR')
383 assert len(email
.errors
) == 1
384 assert email
.errors
[0].message
== 'missing description of a change'
386 def test_emptry_entry_desc_2(self
):
387 email
= self
.from_patch_glob('0001-lto-fix-LTO-debug')
388 assert not email
.errors
389 assert len(email
.changelog_entries
) == 1
391 def test_wildcard_in_subdir(self
):
392 email
= self
.from_patch_glob('0001-Wildcard-subdirs.patch')
393 assert len(email
.changelog_entries
) == 1
394 err
= email
.errors
[0]
395 assert err
.message
== "pattern doesn't match any changed files"
396 assert err
.line
== 'libstdc++-v3/testsuite/28_regex_not-existing/'
398 def test_unicode_chars_in_filename(self
):
399 email
= self
.from_patch_glob('0001-Add-horse.patch')
400 assert not email
.errors
402 def test_bad_unicode_chars_in_filename(self
):
403 email
= self
.from_patch_glob('0001-Add-horse2.patch')
404 assert not email
.errors
405 assert email
.changelog_entries
[0].files
== ['koníček.txt']
407 def test_modification_of_old_changelog(self
):
408 email
= self
.from_patch_glob('0001-fix-old-ChangeLog.patch')
409 assert not email
.errors
411 def test_multiline_parentheses(self
):
412 email
= self
.from_patch_glob('0001-Add-macro.patch')
413 assert not email
.errors
415 def test_multiline_bad_parentheses(self
):
416 email
= self
.from_patch_glob('0002-Wrong-macro-changelog.patch')
417 assert email
.errors
[0].message
== 'bad parentheses wrapping'
418 assert email
.errors
[0].line
== ' * config/i386/i386.md (*fix_trunc<mode>_i387_1,'
420 def test_changelog_removal(self
):
421 email
= self
.from_patch_glob('0001-ChangeLog-removal.patch')
422 assert not email
.errors
424 def test_long_filenames(self
):
425 email
= self
.from_patch_glob('0001-long-filenames')
426 assert not email
.errors
428 def test_multi_same_file(self
):
429 email
= self
.from_patch_glob('0001-OpenMP-Fix-SIMT')
430 assert email
.errors
[0].message
== 'same file specified multiple times'
432 def test_pr_only_in_subject(self
):
433 email
= self
.from_patch_glob('0001-rs6000-Support-doubleword')
434 assert (email
.errors
[0].message
==
435 'PR 100085 in subject but not in changelog')
437 def test_wrong_pr_comp_in_subject(self
):
438 email
= self
.from_patch_glob('pr-wrong-comp.patch')
439 assert email
.errors
[0].message
== 'invalid PR component in subject'
441 def test_copyright_years(self
):
442 email
= self
.from_patch_glob('copyright-years.patch')
443 assert not email
.errors
445 def test_non_ascii_email(self
):
446 email
= self
.from_patch_glob('non-ascii-email.patch')
447 assert (email
.errors
[0].message
==
448 'non-ASCII characters in git commit email address (jbglaw@ług-owl.de)')
450 def test_new_file_in_root_folder(self
):
451 email
= self
.from_patch_glob('toplev-new-file.patch')
452 assert (email
.errors
[0].message
==
453 'new file in the top-level folder not mentioned in a ChangeLog')