1# Copyright © 2019-2020 Intel Corporation 2 3# Permission is hereby granted, free of charge, to any person obtaining a copy 4# of this software and associated documentation files (the "Software"), to deal 5# in the Software without restriction, including without limitation the rights 6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7# copies of the Software, and to permit persons to whom the Software is 8# furnished to do so, subject to the following conditions: 9 10# The above copyright notice and this permission notice shall be included in 11# all copies or substantial portions of the Software. 12 13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19# SOFTWARE. 20 21"""Tests for pick's core data structures and routines.""" 22 23from unittest import mock 24import textwrap 25import typing 26 27import attr 28import pytest 29 30from . import core 31 32 33class TestCommit: 34 35 @pytest.fixture 36 def unnominated_commit(self) -> 'core.Commit': 37 return core.Commit('abc123', 'sub: A commit', main_sha='45678') 38 39 @pytest.fixture 40 def nominated_commit(self) -> 'core.Commit': 41 return core.Commit('abc123', 'sub: A commit', True, 42 core.NominationType.CC, core.Resolution.UNRESOLVED) 43 44 class TestToJson: 45 46 def test_not_nominated(self, unnominated_commit: 'core.Commit'): 47 c = unnominated_commit 48 v = c.to_json() 49 assert v == {'sha': 'abc123', 'description': 'sub: A commit', 'nominated': False, 50 'nomination_type': None, 'resolution': core.Resolution.UNRESOLVED.value, 51 'main_sha': '45678', 'because_sha': None} 52 53 def test_nominated(self, nominated_commit: 'core.Commit'): 54 c = nominated_commit 55 v = c.to_json() 56 assert v == {'sha': 'abc123', 57 'description': 'sub: A commit', 58 'nominated': True, 59 'nomination_type': core.NominationType.CC.value, 60 'resolution': core.Resolution.UNRESOLVED.value, 61 'main_sha': None, 62 'because_sha': None} 63 64 class TestFromJson: 65 66 def test_not_nominated(self, unnominated_commit: 'core.Commit'): 67 c = unnominated_commit 68 v = c.to_json() 69 c2 = core.Commit.from_json(v) 70 assert c == c2 71 72 def test_nominated(self, nominated_commit: 'core.Commit'): 73 c = nominated_commit 74 v = c.to_json() 75 c2 = core.Commit.from_json(v) 76 assert c == c2 77 78 79class TestRE: 80 81 """Tests for the regular expressions used to identify commits.""" 82 83 class TestFixes: 84 85 def test_simple(self): 86 message = textwrap.dedent("""\ 87 etnaviv: fix vertex buffer state emission for single stream GPUs 88 89 GPUs with a single supported vertex stream must use the single state 90 address to program the stream. 91 92 Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5) 93 Signed-off-by: Lucas Stach <[email protected]> 94 Reviewed-by: Jonathan Marek <[email protected]> 95 """) 96 97 fix_for_commit = core.IS_FIX.search(message) 98 assert fix_for_commit is not None 99 assert fix_for_commit.group(1) == '3d09bb390a39' 100 101 class TestCC: 102 103 def test_single_branch(self): 104 """Tests commit meant for a single branch, ie, 19.1""" 105 message = textwrap.dedent("""\ 106 radv: fix DCC fast clear code for intensity formats 107 108 This fixes a rendering issue with DiRT 4 on GFX10. Only GFX10 was 109 affected because intensity formats are different. 110 111 Cc: 19.2 <[email protected]> 112 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1923 113 Signed-off-by: Samuel Pitoiset <[email protected]> 114 Reviewed-by: Bas Nieuwenhuizen <[email protected]> 115 """) 116 117 cc_to = core.IS_CC.search(message) 118 assert cc_to is not None 119 assert cc_to.group(1) == '19.2' 120 121 def test_multiple_branches(self): 122 """Tests commit with more than one branch specified""" 123 message = textwrap.dedent("""\ 124 radeonsi: enable zerovram for Rocket League 125 126 Fixes corruption on game startup. 127 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1888 128 129 Cc: 19.1 19.2 <[email protected]> 130 Reviewed-by: Pierre-Eric Pelloux-Prayer <[email protected]> 131 """) 132 133 cc_to = core.IS_CC.search(message) 134 assert cc_to is not None 135 assert cc_to.group(1) == '19.1' 136 assert cc_to.group(2) == '19.2' 137 138 def test_no_branch(self): 139 """Tests commit with no branch specification""" 140 message = textwrap.dedent("""\ 141 anv/android: fix images created with external format support 142 143 This fixes a case where user first creates image and then later binds it 144 with memory created from AHW buffer. 145 146 Cc: <[email protected]> 147 Signed-off-by: Tapani Pälli <[email protected]> 148 Reviewed-by: Lionel Landwerlin <[email protected]> 149 """) 150 151 cc_to = core.IS_CC.search(message) 152 assert cc_to is not None 153 154 def test_quotes(self): 155 """Tests commit with quotes around the versions""" 156 message = textwrap.dedent("""\ 157 anv: Always fill out the AUX table even if CCS is disabled 158 159 Cc: "20.0" [email protected] 160 Reviewed-by: Kenneth Graunke <[email protected]> 161 Tested-by: Marge Bot <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 162 Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 163 """) 164 165 cc_to = core.IS_CC.search(message) 166 assert cc_to is not None 167 assert cc_to.group(1) == '20.0' 168 169 def test_multiple_quotes(self): 170 """Tests commit with quotes around the versions""" 171 message = textwrap.dedent("""\ 172 anv: Always fill out the AUX table even if CCS is disabled 173 174 Cc: "20.0" "20.1" [email protected] 175 Reviewed-by: Kenneth Graunke <[email protected]> 176 Tested-by: Marge Bot <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 177 Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 178 """) 179 180 cc_to = core.IS_CC.search(message) 181 assert cc_to is not None 182 assert cc_to.group(1) == '20.0' 183 assert cc_to.group(2) == '20.1' 184 185 def test_single_quotes(self): 186 """Tests commit with quotes around the versions""" 187 message = textwrap.dedent("""\ 188 anv: Always fill out the AUX table even if CCS is disabled 189 190 Cc: '20.0' [email protected] 191 Reviewed-by: Kenneth Graunke <[email protected]> 192 Tested-by: Marge Bot <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 193 Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 194 """) 195 196 cc_to = core.IS_CC.search(message) 197 assert cc_to is not None 198 assert cc_to.group(1) == '20.0' 199 200 def test_multiple_single_quotes(self): 201 """Tests commit with quotes around the versions""" 202 message = textwrap.dedent("""\ 203 anv: Always fill out the AUX table even if CCS is disabled 204 205 Cc: '20.0' '20.1' [email protected] 206 Reviewed-by: Kenneth Graunke <[email protected]> 207 Tested-by: Marge Bot <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 208 Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3454> 209 """) 210 211 cc_to = core.IS_CC.search(message) 212 assert cc_to is not None 213 assert cc_to.group(1) == '20.0' 214 assert cc_to.group(2) == '20.1' 215 216 class TestRevert: 217 218 def test_simple(self): 219 message = textwrap.dedent("""\ 220 Revert "radv: do not emit PKT3_CONTEXT_CONTROL with AMDGPU 3.6.0+" 221 222 This reverts commit 2ca8629fa9b303e24783b76a7b3b0c2513e32fbd. 223 224 This was initially ported from RadeonSI, but in the meantime it has 225 been reverted because it might hang. Be conservative and re-introduce 226 this packet emission. 227 228 Unfortunately this doesn't fix anything known. 229 230 Cc: 19.2 <[email protected]> 231 Signed-off-by: Samuel Pitoiset <[email protected]> 232 Reviewed-by: Bas Nieuwenhuizen <[email protected]> 233 """) 234 235 revert_of = core.IS_REVERT.search(message) 236 assert revert_of is not None 237 assert revert_of.group(1) == '2ca8629fa9b303e24783b76a7b3b0c2513e32fbd' 238 239 class TestBackportTo: 240 241 def test_single_release(self): 242 """Tests commit meant for a single branch, ie, 19.1""" 243 message = textwrap.dedent("""\ 244 radv: fix DCC fast clear code for intensity formats 245 246 This fixes a rendering issue with DiRT 4 on GFX10. Only GFX10 was 247 affected because intensity formats are different. 248 249 Backport-to: 19.2 250 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1923 251 Signed-off-by: Samuel Pitoiset <[email protected]> 252 Reviewed-by: Bas Nieuwenhuizen <[email protected]> 253 """) 254 255 backport_to = core.IS_BACKPORT.search(message) 256 assert backport_to is not None 257 assert backport_to.groups() == ('19.2', None) 258 259 def test_multiple_release_space(self): 260 """Tests commit with more than one branch specified""" 261 message = textwrap.dedent("""\ 262 radeonsi: enable zerovram for Rocket League 263 264 Fixes corruption on game startup. 265 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1888 266 267 Backport-to: 19.1 19.2 268 Reviewed-by: Pierre-Eric Pelloux-Prayer <[email protected]> 269 """) 270 271 backport_to = core.IS_BACKPORT.search(message) 272 assert backport_to is not None 273 assert backport_to.groups() == ('19.1', '19.2') 274 275 def test_multiple_release_comma(self): 276 """Tests commit with more than one branch specified""" 277 message = textwrap.dedent("""\ 278 radeonsi: enable zerovram for Rocket League 279 280 Fixes corruption on game startup. 281 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/1888 282 283 Backport-to: 19.1, 19.2 284 Reviewed-by: Pierre-Eric Pelloux-Prayer <[email protected]> 285 """) 286 287 backport_to = core.IS_BACKPORT.search(message) 288 assert backport_to is not None 289 assert backport_to.groups() == ('19.1', '19.2') 290 291 292class TestResolveNomination: 293 294 @attr.s(slots=True) 295 class FakeSubprocess: 296 297 """A fake asyncio.subprocess like class for use with mock.""" 298 299 out: typing.Optional[bytes] = attr.ib(None) 300 returncode: int = attr.ib(0) 301 302 async def mock(self, *_, **__): 303 """A dirtly little helper for mocking.""" 304 return self 305 306 async def communicate(self) -> typing.Tuple[bytes, bytes]: 307 assert self.out is not None 308 return self.out, b'' 309 310 async def wait(self) -> int: 311 return self.returncode 312 313 @staticmethod 314 async def return_true(*_, **__) -> bool: 315 return True 316 317 @staticmethod 318 async def return_false(*_, **__) -> bool: 319 return False 320 321 @pytest.mark.asyncio 322 async def test_fix_is_nominated(self): 323 s = self.FakeSubprocess(b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)') 324 c = core.Commit('abcdef1234567890', 'a commit') 325 326 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 327 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 328 await core.resolve_nomination(c, '') 329 330 assert c.nominated 331 assert c.nomination_type is core.NominationType.FIXES 332 333 @pytest.mark.asyncio 334 async def test_fix_is_not_nominated(self): 335 s = self.FakeSubprocess(b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)') 336 c = core.Commit('abcdef1234567890', 'a commit') 337 338 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 339 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_false): 340 await core.resolve_nomination(c, '') 341 342 assert not c.nominated 343 assert c.nomination_type is core.NominationType.FIXES 344 345 @pytest.mark.asyncio 346 async def test_cc_is_nominated(self): 347 s = self.FakeSubprocess(b'Cc: 16.2 <[email protected]>') 348 c = core.Commit('abcdef1234567890', 'a commit') 349 350 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 351 await core.resolve_nomination(c, '16.2') 352 353 assert c.nominated 354 assert c.nomination_type is core.NominationType.CC 355 356 @pytest.mark.asyncio 357 async def test_cc_is_nominated2(self): 358 s = self.FakeSubprocess(b'Cc: [email protected]') 359 c = core.Commit('abcdef1234567890', 'a commit') 360 361 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 362 await core.resolve_nomination(c, '16.2') 363 364 assert c.nominated 365 assert c.nomination_type is core.NominationType.CC 366 367 @pytest.mark.asyncio 368 async def test_cc_is_not_nominated(self): 369 s = self.FakeSubprocess(b'Cc: 16.2 <[email protected]>') 370 c = core.Commit('abcdef1234567890', 'a commit') 371 372 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 373 await core.resolve_nomination(c, '16.1') 374 375 assert not c.nominated 376 assert c.nomination_type is None 377 378 @pytest.mark.asyncio 379 async def test_backport_is_nominated(self): 380 s = self.FakeSubprocess(b'Backport-to: 16.2') 381 c = core.Commit('abcdef1234567890', 'a commit') 382 383 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 384 await core.resolve_nomination(c, '16.2') 385 386 assert c.nominated 387 assert c.nomination_type is core.NominationType.BACKPORT 388 389 @pytest.mark.asyncio 390 async def test_backport_is_not_nominated(self): 391 s = self.FakeSubprocess(b'Backport-to: 16.2') 392 c = core.Commit('abcdef1234567890', 'a commit') 393 394 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 395 await core.resolve_nomination(c, '16.1') 396 397 assert not c.nominated 398 assert c.nomination_type is None 399 400 @pytest.mark.asyncio 401 async def test_revert_is_nominated(self): 402 s = self.FakeSubprocess(b'This reverts commit 1234567890123456789012345678901234567890.') 403 c = core.Commit('abcdef1234567890', 'a commit') 404 405 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 406 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 407 await core.resolve_nomination(c, '') 408 409 assert c.nominated 410 assert c.nomination_type is core.NominationType.REVERT 411 412 @pytest.mark.asyncio 413 async def test_revert_is_not_nominated(self): 414 s = self.FakeSubprocess(b'This reverts commit 1234567890123456789012345678901234567890.') 415 c = core.Commit('abcdef1234567890', 'a commit') 416 417 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 418 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_false): 419 await core.resolve_nomination(c, '') 420 421 assert not c.nominated 422 assert c.nomination_type is core.NominationType.REVERT 423 424 @pytest.mark.asyncio 425 async def test_is_fix_and_backport(self): 426 s = self.FakeSubprocess( 427 b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)\n' 428 b'Backport-to: 16.1' 429 ) 430 c = core.Commit('abcdef1234567890', 'a commit') 431 432 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 433 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 434 await core.resolve_nomination(c, '16.1') 435 436 assert c.nominated 437 assert c.nomination_type is core.NominationType.FIXES 438 439 @pytest.mark.asyncio 440 async def test_is_fix_and_cc(self): 441 s = self.FakeSubprocess( 442 b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)\n' 443 b'Cc: 16.1 <[email protected]>' 444 ) 445 c = core.Commit('abcdef1234567890', 'a commit') 446 447 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 448 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 449 await core.resolve_nomination(c, '16.1') 450 451 assert c.nominated 452 assert c.nomination_type is core.NominationType.FIXES 453 454 @pytest.mark.asyncio 455 async def test_is_fix_and_revert(self): 456 s = self.FakeSubprocess( 457 b'Fixes: 3d09bb390a39 (etnaviv: GC7000: State changes for HALTI3..5)\n' 458 b'This reverts commit 1234567890123456789012345678901234567890.' 459 ) 460 c = core.Commit('abcdef1234567890', 'a commit') 461 462 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 463 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 464 await core.resolve_nomination(c, '16.1') 465 466 assert c.nominated 467 assert c.nomination_type is core.NominationType.FIXES 468 469 @pytest.mark.asyncio 470 async def test_is_cc_and_revert(self): 471 s = self.FakeSubprocess( 472 b'This reverts commit 1234567890123456789012345678901234567890.\n' 473 b'Cc: 16.1 <[email protected]>' 474 ) 475 c = core.Commit('abcdef1234567890', 'a commit') 476 477 with mock.patch('bin.pick.core.asyncio.create_subprocess_exec', s.mock): 478 with mock.patch('bin.pick.core.is_commit_in_branch', self.return_true): 479 await core.resolve_nomination(c, '16.1') 480 481 assert c.nominated 482 assert c.nomination_type is core.NominationType.CC 483 484 485class TestResolveFixes: 486 487 @pytest.mark.asyncio 488 async def test_in_new(self): 489 """Because commit abcd is nominated, so f123 should be as well.""" 490 c = [ 491 core.Commit('f123', 'desc', nomination_type=core.NominationType.FIXES, because_sha='abcd'), 492 core.Commit('abcd', 'desc', True), 493 ] 494 await core.resolve_fixes(c, []) 495 assert c[1].nominated 496 497 @pytest.mark.asyncio 498 async def test_not_in_new(self): 499 """Because commit abcd is not nominated, commit f123 shouldn't be either.""" 500 c = [ 501 core.Commit('f123', 'desc', nomination_type=core.NominationType.FIXES, because_sha='abcd'), 502 core.Commit('abcd', 'desc'), 503 ] 504 await core.resolve_fixes(c, []) 505 assert not c[0].nominated 506 507 @pytest.mark.asyncio 508 async def test_in_previous(self): 509 """Because commit abcd is nominated, so f123 should be as well.""" 510 p = [ 511 core.Commit('abcd', 'desc', True), 512 ] 513 c = [ 514 core.Commit('f123', 'desc', nomination_type=core.NominationType.FIXES, because_sha='abcd'), 515 ] 516 await core.resolve_fixes(c, p) 517 assert c[0].nominated 518 519 @pytest.mark.asyncio 520 async def test_not_in_previous(self): 521 """Because commit abcd is not nominated, commit f123 shouldn't be either.""" 522 p = [ 523 core.Commit('abcd', 'desc'), 524 ] 525 c = [ 526 core.Commit('f123', 'desc', nomination_type=core.NominationType.FIXES, because_sha='abcd'), 527 ] 528 await core.resolve_fixes(c, p) 529 assert not c[0].nominated 530 531 532class TestIsCommitInBranch: 533 534 @pytest.mark.asyncio 535 async def test_no(self): 536 # Hopefully this is never true? 537 value = await core.is_commit_in_branch('ffffffffffffffffffffffffffffff') 538 assert not value 539 540 @pytest.mark.asyncio 541 async def test_yes(self): 542 # This commit is from 2000, it better always be in the branch 543 value = await core.is_commit_in_branch('88f3b89a2cb77766d2009b9868c44e03abe2dbb2') 544 assert value 545 546 547class TestFullSha: 548 549 @pytest.mark.asyncio 550 async def test_basic(self): 551 # This commit is from 2000, it better always be in the branch 552 value = await core.full_sha('88f3b89a2cb777') 553 assert value 554 555 @pytest.mark.asyncio 556 async def test_invalid(self): 557 # This commit is from 2000, it better always be in the branch 558 with pytest.raises(core.PickUIException): 559 await core.full_sha('fffffffffffffffffffffffffffffffffff') 560