3 # Copyright (C) 2020-2023 Free Software Foundation, Inc.
5 # This file is part of GCC.
7 # GCC is free software; you can redistribute it and/or modify it under
8 # the terms of the GNU General Public License as published by the Free
9 # Software Foundation; either version 3, or (at your option) any later
12 # GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 # You should have received a copy of the GNU General Public License
18 # along with GCC; see the file COPYING3. If not see
19 # <http://www.gnu.org/licenses/>. */
25 from git_commit
import GitCommit
27 from git_email
import GitEmail
31 script_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
33 unidiff_supports_renaming
= hasattr(unidiff
.PatchedFile(), 'is_rename')
37 M gcc/ada/impunit.adb'
38 R097 gcc/ada/libgnat/s-atopar.adb gcc/ada/libgnat/s-aoinar.adb
42 class TestGccChangelog(unittest
.TestCase
):
49 with
open(os
.path
.join(script_path
, 'test_patches.txt'), newline
='\n') as f
:
51 for line
in lines
.split('\n'):
52 if line
.startswith('==='):
54 self
.patches
[filename
] = patch_lines
55 filename
= line
.split(' ')[1]
58 patch_lines
.append(line
)
60 self
.patches
[filename
] = patch_lines
64 assert t
.endswith('.patch')
67 def get_git_email(self
, filename
):
68 with tempfile
.NamedTemporaryFile(mode
='w+', suffix
='.patch',
70 f
.write('\n'.join(self
.patches
[filename
]))
71 self
.temps
.append(f
.name
)
72 return GitEmail(f
.name
)
74 def from_patch_glob(self
, name
):
75 files
= [f
for f
in self
.patches
.keys() if f
.startswith(name
)]
76 assert len(files
) == 1
77 return self
.get_git_email(files
[0])
79 def test_simple_patch_format(self
):
80 email
= self
.get_git_email('0577-aarch64-Add-an-and.patch')
81 assert not email
.errors
82 assert len(email
.changelog_entries
) == 2
83 entry
= email
.changelog_entries
[0]
84 assert (entry
.author_lines
==
85 [('Richard Sandiford <richard.sandiford@arm.com>',
87 assert len(entry
.authors
) == 1
88 assert (entry
.authors
[0]
89 == 'Richard Sandiford <richard.sandiford@arm.com>')
90 assert entry
.folder
== 'gcc'
91 assert entry
.prs
== ['PR target/87763']
92 assert len(entry
.files
) == 3
93 assert entry
.files
[0] == 'config/aarch64/aarch64-protos.h'
95 def test_daily_bump(self
):
96 email
= self
.get_git_email('0085-Daily-bump.patch')
97 assert not email
.errors
98 assert not email
.changelog_entries
100 def test_deduce_changelog_entries(self
):
101 email
= self
.from_patch_glob('0040')
102 assert len(email
.changelog_entries
) == 2
103 assert email
.changelog_entries
[0].folder
== 'gcc/cp'
104 assert email
.changelog_entries
[0].prs
== ['PR c++/90916']
105 assert email
.changelog_entries
[0].files
== ['pt.c']
106 # this one is added automatically
107 assert email
.changelog_entries
[1].folder
== 'gcc/testsuite'
109 def test_only_changelog_updated(self
):
110 email
= self
.from_patch_glob('0129')
111 assert not email
.errors
112 assert not email
.changelog_entries
114 def test_wrong_mentioned_filename(self
):
115 email
= self
.from_patch_glob('0096')
117 err
= email
.errors
[0]
118 assert err
.message
== 'unchanged file mentioned in a ChangeLog (did ' \
119 'you mean "gcc/testsuite/gcc.target/aarch64/' \
120 'advsimd-intrinsics/vdot-3-1.c"?)'
121 assert err
.line
== 'gcc/testsuite/gcc.target/aarch64/' \
122 'advsimd-intrinsics/vdot-compile-3-1.c'
124 def test_missing_tab(self
):
125 email
= self
.from_patch_glob('0031')
126 assert len(email
.errors
) == 2
127 err
= email
.errors
[0]
128 assert err
.message
== 'line should start with a tab'
129 assert err
.line
== ' * cfgloopanal.c (average_num_loop_insns): ' \
130 'Free bbs when early'
132 def test_leading_changelog_format(self
):
133 email
= self
.from_patch_glob('0184')
134 assert len(email
.errors
) == 4
135 assert email
.errors
[0].line
== 'gcc/c-family/c-cppbuiltins.c'
136 assert email
.errors
[2].line
== 'gcc/c-family/c-cppbuiltin.c'
138 def test_cannot_deduce_no_blank_line(self
):
139 email
= self
.from_patch_glob('0334')
140 assert len(email
.errors
) == 1
141 assert len(email
.changelog_entries
) == 1
142 assert email
.changelog_entries
[0].folder
is None
144 def test_author_lines(self
):
145 email
= self
.from_patch_glob('0814')
146 assert not email
.errors
147 assert (email
.changelog_entries
[0].author_lines
==
148 [('Martin Jambor <mjambor@suse.cz>', '2020-02-19')])
150 def test_multiple_authors_and_prs(self
):
151 email
= self
.from_patch_glob('0735')
152 assert len(email
.changelog_entries
) == 1
153 entry
= email
.changelog_entries
[0]
154 assert len(entry
.author_lines
) == 2
155 assert len(entry
.authors
) == 2
156 assert (entry
.author_lines
[1] ==
157 ('Bernd Edlinger <bernd.edlinger@hotmail.de>', None))
159 def test_multiple_prs(self
):
160 email
= self
.from_patch_glob('1699')
161 assert len(email
.changelog_entries
) == 2
162 assert len(email
.changelog_entries
[0].prs
) == 2
164 def test_missing_PR_component(self
):
165 email
= self
.from_patch_glob('0735')
166 assert len(email
.errors
) == 1
167 assert email
.errors
[0].message
== 'missing PR component'
169 def test_invalid_PR_component(self
):
170 email
= self
.from_patch_glob('0198')
171 assert len(email
.errors
) == 1
172 assert email
.errors
[0].message
== 'invalid PR component'
174 def test_additional_author_list(self
):
175 email
= self
.from_patch_glob('0342')
176 msg
= 'additional author must be indented ' \
177 'with one tab and four spaces'
178 assert email
.errors
[1].message
== msg
180 def test_trailing_whitespaces(self
):
181 email
= self
.get_git_email('trailing-whitespaces.patch')
182 assert len(email
.errors
) == 3
184 def test_space_after_asterisk(self
):
185 email
= self
.from_patch_glob('1999')
186 assert len(email
.errors
) == 1
187 assert email
.errors
[0].message
== 'one space should follow asterisk'
189 def test_long_lines(self
):
190 email
= self
.get_git_email('long-lines.patch')
191 assert len(email
.errors
) == 1
192 assert email
.errors
[0].message
== 'line exceeds 100 character limit'
194 def test_new_files(self
):
195 email
= self
.from_patch_glob('0030')
196 assert not email
.errors
198 def test_wrong_changelog_location(self
):
199 email
= self
.from_patch_glob('0043')
200 assert len(email
.errors
) == 2
201 assert (email
.errors
[0].message
==
202 'wrong ChangeLog location "gcc", should be "gcc/testsuite"')
204 def test_single_author_name(self
):
205 email
= self
.from_patch_glob('1975')
206 assert len(email
.changelog_entries
) == 2
207 assert len(email
.changelog_entries
[0].author_lines
) == 1
208 assert len(email
.changelog_entries
[1].author_lines
) == 1
210 def test_bad_first_line(self
):
211 email
= self
.from_patch_glob('0413')
212 assert len(email
.errors
) == 1
214 def test_co_authored_by(self
):
215 email
= self
.from_patch_glob('1850')
216 assert email
.co_authors
== ['Jakub Jelinek <jakub@redhat.com>']
217 output_entries
= list(email
.to_changelog_entries())
218 assert len(output_entries
) == 2
219 ent0
= output_entries
[0]
220 assert ent0
[1].startswith('2020-04-16 Martin Liska '
221 '<mliska@suse.cz>\n\t'
222 ' Jakub Jelinek <jakub@redhat.com>')
224 def test_multiple_co_author_formats(self
):
225 email
= self
.get_git_email('co-authored-by.patch')
226 assert len(email
.co_authors
) == 3
227 assert email
.co_authors
[0] == 'Jakub Jelinek <jakub@redhat.com>'
228 assert email
.co_authors
[1] == 'John Miller <jm@example.com>'
229 assert email
.co_authors
[2] == 'John Miller2 <jm2@example.com>'
231 def test_new_file_added_entry(self
):
232 email
= self
.from_patch_glob('1957')
233 output_entries
= list(email
.to_changelog_entries())
234 assert len(output_entries
) == 2
235 needle
= ('\t* g++.dg/cpp2a/lambda-generic-variadic20.C'
237 assert output_entries
[1][1].endswith(needle
)
238 assert email
.changelog_entries
[1].prs
== ['PR c++/94546']
240 def test_global_pr_entry(self
):
241 email
= self
.from_patch_glob('2004')
242 assert not email
.errors
243 assert email
.changelog_entries
[0].prs
== ['PR other/94629']
245 def test_unique_prs(self
):
246 email
= self
.get_git_email('pr-check1.patch')
247 assert not email
.errors
248 assert email
.changelog_entries
[0].prs
== ['PR ipa/12345']
249 assert email
.changelog_entries
[1].prs
== []
251 def test_multiple_prs_not_added(self
):
252 email
= self
.from_patch_glob('0002-Add-patch_are')
253 assert not email
.errors
254 assert email
.changelog_entries
[0].prs
== ['PR target/93492']
255 assert email
.changelog_entries
[1].prs
== ['PR target/12345']
256 assert email
.changelog_entries
[2].prs
== []
257 assert email
.changelog_entries
[2].folder
== 'gcc/testsuite'
259 def test_strict_mode(self
):
260 email
= self
.from_patch_glob('0001-Add-patch_are')
261 msg
= 'ChangeLog, DATESTAMP, BASE-VER and DEV-PHASE updates should ' \
262 'be done separately from normal commits'
263 assert email
.errors
[0].message
.startswith(msg
)
265 def test_strict_mode_normal_patch(self
):
266 email
= self
.get_git_email('0001-Just-test-it.patch')
267 assert not email
.errors
269 def test_strict_mode_datestamp_only(self
):
270 email
= self
.get_git_email('0002-Bump-date.patch')
271 assert not email
.errors
273 def test_wrong_changelog_entry(self
):
274 email
= self
.from_patch_glob('0020-IPA-Avoid')
275 msg
= 'first line should start with a tab, an asterisk and a space'
276 assert (email
.errors
[0].message
== msg
)
278 def test_cherry_pick_format(self
):
279 email
= self
.from_patch_glob('0001-c-Alias.patch')
280 assert not email
.errors
282 def test_signatures(self
):
283 email
= self
.from_patch_glob('0001-RISC-V-Make-unique.patch')
284 assert not email
.errors
285 assert len(email
.changelog_entries
) == 1
287 def test_duplicate_top_level_author(self
):
288 email
= self
.from_patch_glob('0001-Fortran-ProcPtr-function.patch')
289 assert not email
.errors
290 assert len(email
.changelog_entries
[0].author_lines
) == 1
292 def test_dr_entry(self
):
293 email
= self
.from_patch_glob('0001-c-C-20-DR-2237.patch')
294 assert email
.changelog_entries
[0].prs
== ['DR 2237']
296 def test_changes_only_in_ignored_location(self
):
297 email
= self
.from_patch_glob('0001-go-in-ignored-location.patch')
298 assert not email
.errors
300 def test_changelog_for_ignored_location(self
):
301 email
= self
.from_patch_glob('0001-Update-merge.sh-to-reflect.patch')
302 assert (email
.changelog_entries
[0].lines
[0]
303 == '\t* LOCAL_PATCHES: Use git hash instead of SVN id.')
305 def test_multiline_file_list(self
):
306 email
= self
.from_patch_glob(
307 '0001-Ada-Reuse-Is_Package_Or_Generic_Package-where-possib.patch')
308 assert (email
.changelog_entries
[0].files
309 == ['contracts.adb', 'einfo.adb', 'exp_ch9.adb',
310 'sem_ch12.adb', 'sem_ch4.adb', 'sem_ch7.adb',
311 'sem_ch8.adb', 'sem_elab.adb', 'sem_type.adb',
314 @unittest.skipIf(not unidiff_supports_renaming
,
315 'Newer version of unidiff is needed (0.6.0+)')
316 def test_renamed_file(self
):
317 email
= self
.from_patch_glob(
318 '0001-Ada-Add-support-for-XDR-streaming-in-the-default-run.patch')
319 assert not email
.errors
321 def test_duplicite_author_lines(self
):
322 email
= self
.from_patch_glob('0001-Fortran-type-is-real-kind-1.patch')
323 assert (email
.changelog_entries
[0].author_lines
[0][0]
324 == 'Steven G. Kargl <kargl@gcc.gnu.org>')
325 assert (email
.changelog_entries
[0].author_lines
[1][0]
326 == 'Mark Eggleston <markeggleston@gcc.gnu.org>')
328 def test_missing_change_description(self
):
329 email
= self
.from_patch_glob('0001-Missing-change-description.patch')
330 assert len(email
.errors
) == 2
331 assert email
.errors
[0].message
== 'missing description of a change'
332 assert email
.errors
[1].message
== 'missing description of a change'
334 def test_libstdcxx_html_regenerated(self
):
335 email
= self
.from_patch_glob('0001-Fix-text-of-hyperlink')
336 assert not email
.errors
337 email
= self
.from_patch_glob('0002-libstdc-Fake-test-change-1.patch')
338 assert len(email
.errors
) == 1
339 msg
= "pattern doesn't match any changed files"
340 assert email
.errors
[0].message
== msg
341 assert email
.errors
[0].line
== 'libstdc++-v3/doc/html/'
342 email
= self
.from_patch_glob('0003-libstdc-Fake-test-change-2.patch')
343 assert len(email
.errors
) == 1
344 msg
= 'changed file not mentioned in a ChangeLog'
345 assert email
.errors
[0].message
== msg
347 def test_not_deduce(self
):
348 email
= self
.from_patch_glob('0001-configure.patch')
349 assert not email
.errors
350 assert len(email
.changelog_entries
) == 2
352 def test_parse_git_name_status(self
):
353 modified_files
= GitCommit
.parse_git_name_status(NAME_STATUS1
)
354 assert len(modified_files
) == 3
355 assert modified_files
[1] == ('gcc/ada/libgnat/s-atopar.adb', 'D')
356 assert modified_files
[2] == ('gcc/ada/libgnat/s-aoinar.adb', 'A')
358 def test_backport(self
):
359 email
= self
.from_patch_glob('0001-asan-fix-RTX-emission.patch')
360 assert not email
.errors
361 expected_hash
= '8cff672cb9a132d3d3158c2edfc9a64b55292b80'
362 assert email
.cherry_pick_commit
== expected_hash
363 assert len(email
.changelog_entries
) == 1
364 entry
= list(email
.to_changelog_entries())[0][1]
365 assert entry
.startswith('2020-06-11 Martin Liska <mliska@suse.cz>')
366 assert '\tBackported from master:' in entry
367 assert '\t2020-06-11 Martin Liska <mliska@suse.cz>' in entry
368 assert '\t\t Jakub Jelinek <jakub@redhat.com>' in entry
370 def test_backport_double_cherry_pick(self
):
371 email
= self
.from_patch_glob('double-cherry-pick.patch')
372 assert email
.errors
[0].message
.startswith('multiple cherry pick lines')
374 def test_square_and_lt_gt(self
):
375 email
= self
.from_patch_glob('0001-Check-for-more-missing')
376 assert not email
.errors
378 def test_empty_parenthesis(self
):
379 email
= self
.from_patch_glob('0001-tree-optimization-97633-fix')
380 assert len(email
.errors
) == 1
381 assert email
.errors
[0].message
== 'empty group "()" found'
383 def test_emptry_entry_desc(self
):
384 email
= self
.from_patch_glob('0001-c-Set-CALL_FROM_NEW_OR')
385 assert len(email
.errors
) == 1
386 assert email
.errors
[0].message
== 'missing description of a change'
388 def test_emptry_entry_desc_2(self
):
389 email
= self
.from_patch_glob('0001-lto-fix-LTO-debug')
390 assert not email
.errors
391 assert len(email
.changelog_entries
) == 1
393 def test_wildcard_in_subdir(self
):
394 email
= self
.from_patch_glob('0001-Wildcard-subdirs.patch')
395 assert len(email
.changelog_entries
) == 1
396 err
= email
.errors
[0]
397 assert err
.message
== "pattern doesn't match any changed files"
398 assert err
.line
== 'libstdc++-v3/testsuite/28_regex_not-existing/'
400 def test_unicode_chars_in_filename(self
):
401 email
= self
.from_patch_glob('0001-Add-horse.patch')
402 assert not email
.errors
404 def test_bad_unicode_chars_in_filename(self
):
405 email
= self
.from_patch_glob('0001-Add-horse2.patch')
406 assert not email
.errors
407 assert email
.changelog_entries
[0].files
== ['koníček.txt']
409 def test_modification_of_old_changelog(self
):
410 email
= self
.from_patch_glob('0001-fix-old-ChangeLog.patch')
411 assert not email
.errors
413 def test_multiline_parentheses(self
):
414 email
= self
.from_patch_glob('0001-Add-macro.patch')
415 assert not email
.errors
417 def test_multiline_bad_parentheses(self
):
418 email
= self
.from_patch_glob('0002-Wrong-macro-changelog.patch')
419 assert email
.errors
[0].message
== 'bad parentheses wrapping'
420 assert email
.errors
[0].line
== ' * config/i386/i386.md (*fix_trunc<mode>_i387_1,'
422 def test_changelog_removal(self
):
423 email
= self
.from_patch_glob('0001-ChangeLog-removal.patch')
424 assert not email
.errors
426 def test_long_filenames(self
):
427 email
= self
.from_patch_glob('0001-long-filenames')
428 assert not email
.errors
430 def test_multi_same_file(self
):
431 email
= self
.from_patch_glob('0001-OpenMP-Fix-SIMT')
432 assert email
.errors
[0].message
== 'same file specified multiple times'
434 def test_pr_only_in_subject(self
):
435 email
= self
.from_patch_glob('0001-rs6000-Support-doubleword')
436 assert (email
.errors
[0].message
==
437 'PR 100085 in subject but not in changelog')
439 def test_wrong_pr_comp_in_subject(self
):
440 email
= self
.from_patch_glob('pr-wrong-comp.patch')
441 assert email
.errors
[0].message
== 'invalid PR component in subject'
443 def test_copyright_years(self
):
444 email
= self
.from_patch_glob('copyright-years.patch')
445 assert not email
.errors
447 def test_non_ascii_email(self
):
448 email
= self
.from_patch_glob('non-ascii-email.patch')
449 assert (email
.errors
[0].message
==
450 'non-ASCII characters in git commit email address (jbglaw@ług-owl.de)')
452 def test_new_file_in_root_folder(self
):
453 email
= self
.from_patch_glob('toplev-new-file.patch')
454 assert (email
.errors
[0].message
==
455 'new file in the top-level folder not mentioned in a ChangeLog')
457 def test_space_after_tab(self
):
458 email
= self
.from_patch_glob('0001-Use-Value_Range-when-applying-inferred-ranges.patch')
459 assert (email
.errors
[0].message
== 'extra space after tab')
461 def test_CR_in_patch(self
):
462 email
= self
.from_patch_glob('0001-Add-M-character.patch')
463 assert (email
.errors
[0].message
== 'cannot find a ChangeLog location in message')
465 def test_auto_add_file_1(self
):
466 email
= self
.from_patch_glob('0001-Auto-Add-File.patch')
467 assert not email
.errors
468 assert (len(email
.warnings
) == 1)
469 assert (email
.warnings
[0]
470 == "Auto-added new file 'libgomp/testsuite/libgomp.fortran/allocate-4.f90'")
472 def test_auto_add_file_2(self
):
473 email
= self
.from_patch_glob('0002-Auto-Add-File.patch')
474 assert not email
.errors
475 assert (len(email
.warnings
) == 2)
476 assert (email
.warnings
[0] == "Auto-added new file 'gcc/doc/gm2.texi'")
477 assert (email
.warnings
[1] == "Auto-added 2 new files in 'gcc/m2'")
479 def test_digit_in_PR_component(self
):
480 email
= self
.from_patch_glob('modula-PR-component.patch')
481 assert not email
.errors