xref: /aosp_15_r20/external/mesa3d/bin/pick/core_test.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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