1#!/usr/bin/env python3 2# 3# This file is part of GCC. 4# 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 8# version. 9# 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 13# for more details. 14# 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/>. */ 18 19import os 20import tempfile 21import unittest 22 23from git_commit import GitCommit 24 25from git_email import GitEmail 26 27import unidiff 28 29script_path = os.path.dirname(os.path.realpath(__file__)) 30 31unidiff_supports_renaming = hasattr(unidiff.PatchedFile(), 'is_rename') 32 33 34NAME_STATUS1 = """ 35M gcc/ada/impunit.adb' 36R097 gcc/ada/libgnat/s-atopar.adb gcc/ada/libgnat/s-aoinar.adb 37""" 38 39 40class TestGccChangelog(unittest.TestCase): 41 def setUp(self): 42 self.patches = {} 43 self.temps = [] 44 45 filename = None 46 patch_lines = [] 47 with open(os.path.join(script_path, 'test_patches.txt')) as f: 48 lines = f.read() 49 for line in lines.split('\n'): 50 if line.startswith('==='): 51 if patch_lines: 52 self.patches[filename] = patch_lines 53 filename = line.split(' ')[1] 54 patch_lines = [] 55 else: 56 patch_lines.append(line) 57 if patch_lines: 58 self.patches[filename] = patch_lines 59 60 def tearDown(self): 61 for t in self.temps: 62 assert t.endswith('.patch') 63 os.remove(t) 64 65 def get_git_email(self, filename, strict=False): 66 with tempfile.NamedTemporaryFile(mode='w+', suffix='.patch', 67 delete=False) as f: 68 f.write('\n'.join(self.patches[filename])) 69 self.temps.append(f.name) 70 return GitEmail(f.name, strict) 71 72 def from_patch_glob(self, name, strict=False): 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], strict) 76 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>', 84 '2020-02-06')]) 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' 92 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 97 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' 106 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 111 112 def test_wrong_mentioned_filename(self): 113 email = self.from_patch_glob('0096') 114 assert email.errors 115 err = email.errors[0] 116 assert err.message == 'unchanged file mentioned in a ChangeLog' 117 assert err.line == 'gcc/testsuite/gcc.target/aarch64/' \ 118 'advsimd-intrinsics/vdot-compile-3-1.c' 119 120 def test_missing_tab(self): 121 email = self.from_patch_glob('0031') 122 assert len(email.errors) == 2 123 err = email.errors[0] 124 assert err.message == 'line should start with a tab' 125 assert err.line == ' * cfgloopanal.c (average_num_loop_insns): ' \ 126 'Free bbs when early' 127 128 def test_leading_changelog_format(self): 129 email = self.from_patch_glob('0184') 130 assert len(email.errors) == 4 131 assert email.errors[0].line == 'gcc/c-family/c-cppbuiltins.c' 132 assert email.errors[2].line == 'gcc/c-family/c-cppbuiltin.c' 133 134 def test_cannot_deduce_no_blank_line(self): 135 email = self.from_patch_glob('0334') 136 assert len(email.errors) == 1 137 assert len(email.changelog_entries) == 1 138 assert email.changelog_entries[0].folder is None 139 140 def test_author_lines(self): 141 email = self.from_patch_glob('0814') 142 assert not email.errors 143 assert (email.changelog_entries[0].author_lines == 144 [('Martin Jambor <mjambor@suse.cz>', '2020-02-19')]) 145 146 def test_multiple_authors_and_prs(self): 147 email = self.from_patch_glob('0735') 148 assert len(email.changelog_entries) == 1 149 entry = email.changelog_entries[0] 150 assert len(entry.author_lines) == 2 151 assert len(entry.authors) == 2 152 assert (entry.author_lines[1] == 153 ('Bernd Edlinger <bernd.edlinger@hotmail.de>', None)) 154 155 def test_multiple_prs(self): 156 email = self.from_patch_glob('1699') 157 assert len(email.changelog_entries) == 2 158 assert len(email.changelog_entries[0].prs) == 2 159 160 def test_missing_PR_component(self): 161 email = self.from_patch_glob('0735') 162 assert len(email.errors) == 1 163 assert email.errors[0].message == 'missing PR component' 164 165 def test_invalid_PR_component(self): 166 email = self.from_patch_glob('0198') 167 assert len(email.errors) == 1 168 assert email.errors[0].message == 'invalid PR component' 169 170 def test_additional_author_list(self): 171 email = self.from_patch_glob('0342') 172 msg = 'additional author must be indented ' \ 173 'with one tab and four spaces' 174 assert email.errors[1].message == msg 175 176 def test_trailing_whitespaces(self): 177 email = self.get_git_email('trailing-whitespaces.patch') 178 assert len(email.errors) == 3 179 180 def test_space_after_asterisk(self): 181 email = self.from_patch_glob('1999') 182 assert len(email.errors) == 1 183 assert email.errors[0].message == 'one space should follow asterisk' 184 185 def test_long_lines(self): 186 email = self.get_git_email('long-lines.patch') 187 assert len(email.errors) == 1 188 assert email.errors[0].message == 'line exceeds 100 character limit' 189 190 def test_new_files(self): 191 email = self.from_patch_glob('0030') 192 assert not email.errors 193 194 def test_wrong_changelog_location(self): 195 email = self.from_patch_glob('0043') 196 assert len(email.errors) == 2 197 assert (email.errors[0].message == 198 'wrong ChangeLog location "gcc", should be "gcc/testsuite"') 199 200 def test_single_author_name(self): 201 email = self.from_patch_glob('1975') 202 assert len(email.changelog_entries) == 2 203 assert len(email.changelog_entries[0].author_lines) == 1 204 assert len(email.changelog_entries[1].author_lines) == 1 205 206 def test_bad_first_line(self): 207 email = self.from_patch_glob('0413') 208 assert len(email.errors) == 1 209 210 def test_co_authored_by(self): 211 email = self.from_patch_glob('1850') 212 assert email.co_authors == ['Jakub Jelinek <jakub@redhat.com>'] 213 output_entries = list(email.to_changelog_entries()) 214 assert len(output_entries) == 2 215 ent0 = output_entries[0] 216 assert ent0[1].startswith('2020-04-16 Martin Liska ' 217 '<mliska@suse.cz>\n\t' 218 ' Jakub Jelinek <jakub@redhat.com>') 219 220 def test_multiple_co_author_formats(self): 221 email = self.get_git_email('co-authored-by.patch') 222 assert len(email.co_authors) == 3 223 assert email.co_authors[0] == 'Jakub Jelinek <jakub@redhat.com>' 224 assert email.co_authors[1] == 'John Miller <jm@example.com>' 225 assert email.co_authors[2] == 'John Miller2 <jm2@example.com>' 226 227 def test_new_file_added_entry(self): 228 email = self.from_patch_glob('1957') 229 output_entries = list(email.to_changelog_entries()) 230 assert len(output_entries) == 2 231 needle = ('\t* g++.dg/cpp2a/lambda-generic-variadic20.C' 232 ': New file.') 233 assert output_entries[1][1].endswith(needle) 234 assert email.changelog_entries[1].prs == ['PR c++/94546'] 235 236 def test_global_pr_entry(self): 237 email = self.from_patch_glob('2004') 238 assert not email.errors 239 assert email.changelog_entries[0].prs == ['PR other/94629'] 240 241 def test_unique_prs(self): 242 email = self.get_git_email('pr-check1.patch') 243 assert not email.errors 244 assert email.changelog_entries[0].prs == ['PR ipa/12345'] 245 assert email.changelog_entries[1].prs == [] 246 247 def test_multiple_prs_not_added(self): 248 email = self.from_patch_glob('0001-Add-patch_are') 249 assert not email.errors 250 assert email.changelog_entries[0].prs == ['PR target/93492'] 251 assert email.changelog_entries[1].prs == ['PR target/12345'] 252 assert email.changelog_entries[2].prs == [] 253 assert email.changelog_entries[2].folder == 'gcc/testsuite' 254 255 def test_strict_mode(self): 256 email = self.from_patch_glob('0001-Add-patch_are', 257 True) 258 msg = 'ChangeLog, DATESTAMP, BASE-VER and DEV-PHASE updates should ' \ 259 'be done separately from normal commits' 260 assert email.errors[0].message == msg 261 262 def test_strict_mode_normal_patch(self): 263 email = self.get_git_email('0001-Just-test-it.patch', True) 264 assert not email.errors 265 266 def test_strict_mode_datestamp_only(self): 267 email = self.get_git_email('0002-Bump-date.patch', True) 268 assert not email.errors 269 270 def test_wrong_changelog_entry(self): 271 email = self.from_patch_glob('0020-IPA-Avoid') 272 msg = 'first line should start with a tab, an asterisk and a space' 273 assert (email.errors[0].message == msg) 274 275 def test_cherry_pick_format(self): 276 email = self.from_patch_glob('0001-c-Alias.patch') 277 assert not email.errors 278 279 def test_signatures(self): 280 email = self.from_patch_glob('0001-RISC-V-Make-unique.patch') 281 assert not email.errors 282 assert len(email.changelog_entries) == 1 283 284 def test_duplicate_top_level_author(self): 285 email = self.from_patch_glob('0001-Fortran-ProcPtr-function.patch') 286 assert not email.errors 287 assert len(email.changelog_entries[0].author_lines) == 1 288 289 def test_dr_entry(self): 290 email = self.from_patch_glob('0001-c-C-20-DR-2237.patch') 291 assert email.changelog_entries[0].prs == ['DR 2237'] 292 293 def test_changes_only_in_ignored_location(self): 294 email = self.from_patch_glob('0001-go-in-ignored-location.patch') 295 assert not email.errors 296 297 def test_changelog_for_ignored_location(self): 298 email = self.from_patch_glob('0001-Update-merge.sh-to-reflect.patch') 299 assert (email.changelog_entries[0].lines[0] 300 == '\t* LOCAL_PATCHES: Use git hash instead of SVN id.') 301 302 def test_multiline_file_list(self): 303 email = self.from_patch_glob( 304 '0001-Ada-Reuse-Is_Package_Or_Generic_Package-where-possib.patch') 305 assert (email.changelog_entries[0].files 306 == ['contracts.adb', 'einfo.adb', 'exp_ch9.adb', 307 'sem_ch12.adb', 'sem_ch4.adb', 'sem_ch7.adb', 308 'sem_ch8.adb', 'sem_elab.adb', 'sem_type.adb', 309 'sem_util.adb']) 310 311 @unittest.skipIf(not unidiff_supports_renaming, 312 'Newer version of unidiff is needed (0.6.0+)') 313 def test_renamed_file(self): 314 email = self.from_patch_glob( 315 '0001-Ada-Add-support-for-XDR-streaming-in-the-default-run.patch') 316 assert not email.errors 317 318 def test_duplicite_author_lines(self): 319 email = self.from_patch_glob('0001-Fortran-type-is-real-kind-1.patch') 320 assert (email.changelog_entries[0].author_lines[0][0] 321 == 'Steven G. Kargl <kargl@gcc.gnu.org>') 322 assert (email.changelog_entries[0].author_lines[1][0] 323 == 'Mark Eggleston <markeggleston@gcc.gnu.org>') 324 325 def test_missing_change_description(self): 326 email = self.from_patch_glob('0001-Missing-change-description.patch') 327 assert len(email.errors) == 2 328 assert email.errors[0].message == 'missing description of a change' 329 assert email.errors[1].message == 'missing description of a change' 330 331 def test_libstdcxx_html_regenerated(self): 332 email = self.from_patch_glob('0001-Fix-text-of-hyperlink') 333 assert not email.errors 334 email = self.from_patch_glob('0002-libstdc-Fake-test-change-1.patch') 335 assert len(email.errors) == 1 336 msg = 'pattern doesn''t match any changed files' 337 assert email.errors[0].message == msg 338 assert email.errors[0].line == 'libstdc++-v3/doc/html/' 339 email = self.from_patch_glob('0003-libstdc-Fake-test-change-2.patch') 340 assert len(email.errors) == 1 341 msg = 'changed file not mentioned in a ChangeLog' 342 assert email.errors[0].message == msg 343 344 def test_not_deduce(self): 345 email = self.from_patch_glob('0001-configure.patch') 346 assert not email.errors 347 assert len(email.changelog_entries) == 2 348 349 def test_parse_git_name_status(self): 350 modified_files = GitCommit.parse_git_name_status(NAME_STATUS1) 351 assert len(modified_files) == 3 352 assert modified_files[1] == ('gcc/ada/libgnat/s-atopar.adb', 'D') 353 assert modified_files[2] == ('gcc/ada/libgnat/s-aoinar.adb', 'A') 354 355 def test_backport(self): 356 email = self.from_patch_glob('0001-asan-fix-RTX-emission.patch') 357 assert not email.errors 358 assert len(email.changelog_entries) == 1 359 entry = list(email.to_changelog_entries())[0][1] 360 assert entry.startswith('2020-06-11 Martin Liska <mliska@suse.cz>') 361 assert '\tBackported from master:' in entry 362 assert '\t2020-06-11 Martin Liska <mliska@suse.cz>' in entry 363 assert '\t\t Jakub Jelinek <jakub@redhat.com>' in entry 364 365 def test_square_and_lt_gt(self): 366 email = self.from_patch_glob('0001-Check-for-more-missing') 367 assert not email.errors 368