diff --git a/bin/ci/test/test_marge_queue.py b/bin/ci/test/test_marge_queue.py new file mode 100644 index 00000000000..de35de17717 --- /dev/null +++ b/bin/ci/test/test_marge_queue.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# Copyright © 2025 Collabora Ltd. +# Authors: +# Sergi Blanch Torne +# +# SPDX-License-Identifier: MIT + +from dataclasses import dataclass +from datetime import timedelta + +from unittest import TestCase +from typing import Iterable + +from marge_queue import MargeMergeRequest, MargeQueue, get_merge_queue + + +DATA = { + 37564: { + # sample of a Merge Request rejected by marge and later reassigned to marge + "updated_at": "2025-09-25T10:59:27.144Z", + "title": "etnaviv: Fix util_blitter_save_so_targets(..) call", + "notes": [ + # Those notes must be listed in reverse order + # because the list is requested sort="desc" + { + "body": "assigned to @marge-bot and unassigned @austriancoder", + "created_at": "2025-09-25T11:44:01.672Z", + }, + { + "body": "added 1 commit", + "created_at": "2025-09-25T10:43:43.490Z", + }, + { + "body": "This branch couldn't be merged:", + "created_at": "2025-09-25T10:42:27.817Z", + }, + { + "body": "assigned to @austriancoder and unassigned @marge-bot", + "created_at": "2025-09-25T10:42:15.404Z", + }, + { + "body": "added 3 commits", + "created_at": "2025-09-25T10:38:57.332Z", + }, + { + "body": "assigned to @marge-bot", + "created_at": "2025-09-25T10:38:03.993Z", + }, + ], + }, + 37560: { + # sample of a Merge Request assigned to marge between the two + # assignments of the previous sample. This is meant to show that + # this goes first in the queue, and it doesn't confuses the sequence. + "updated_at": "2025-09-25T11:38:08.420Z", + "title": "tu: limit query pool types logged into RMV", + "notes": [ + { + "body": "assigned to @marge-bot", + "created_at": "2025-09-25T10:47:40.040Z", + }, + { + "body": "added 1 commit", + "created_at": "2025-09-25T10:46:42.113Z", + }, + { + "body": "mentioned in issue #13970", + "created_at": "2025-09-25T10:45:35.566Z", + }, + { + "body": "Rb", + "created_at": "2025-09-25T08:25:51.527Z", + }, + ], + }, +} + + +@dataclass +class ProjectMergeRequestNote: + body: str + created_at: str + + +@dataclass +class ProjectMergeRequestNoteManager: + """ + Mock the gitlab.v4.objects.ProjectMergeRequestNoteManager + """ + iid: int + + def list(self, *args, **kwargs) -> Iterable: + for note in DATA[self.iid]["notes"]: + yield ProjectMergeRequestNote(**note) + + +@dataclass +class ProjectMergeRequest: + """ + Mock the gitlab.v4.objects.ProjectMergeRequest + """ + iid: int + + @property + def updated_at(self) -> str: + return DATA[self.iid]["updated_at"] + + @property + def web_url(self) -> str: + return f"https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/{self.iid}" + + @property + def title(self) -> str: + return DATA[self.iid]["title"] + + @property + def notes(self) -> ProjectMergeRequestNoteManager: + return ProjectMergeRequestNoteManager(self.iid) + + +@dataclass +class ProjectMergeRequestManager: + """ + Mock the gitlab.v4.objects.ProjectMergeRequestManager + """ + def list(self, *args, **kwargs) -> Iterable: + for mr in DATA.keys(): + yield ProjectMergeRequest(mr) + + +@dataclass +class Project: + """ + Mock the gitlab.v4.objects.Project + """ + @property + def mergerequests(self) -> ProjectMergeRequestManager: + return ProjectMergeRequestManager() + + +class MargeMergeTestCase(TestCase): + def test_queue(self) -> None: + queue = MargeQueue() + for mr_id in DATA.keys(): + gl_mr_mock_obj = ProjectMergeRequest(mr_id) + marge_mr = MargeMergeRequest(gl_mr_mock_obj) + + # for each element in the data sample, test the MargeMergeRequest construction + self.assertEqual(marge_mr.title, gl_mr_mock_obj.title) + self.assertEqual(marge_mr.web_url, gl_mr_mock_obj.web_url) + self.assertIsInstance(marge_mr.time_enqueued, timedelta) + + queue.append(marge_mr) + + self.assertEqual(queue.n_merge_requests_enqueued, 2) + + + def test_get_merge_queue(self) -> None: + project = Project() + queue = get_merge_queue(project) + + self.assertEqual(queue.n_merge_requests_enqueued, 2) + self.assertEqual(len(queue.sorted_queue), 2) + # They are in reverse order because of the reassignment to marge + self.assertEqual(queue.sorted_queue[0].id, list(DATA.keys())[1]) + self.assertEqual(queue.sorted_queue[1].id, list(DATA.keys())[0]) + self.assertEqual(len(queue.undetermined), 0) + + for marge_mr in queue.sorted_queue: + mr_id = marge_mr.id + self.assertEqual(marge_mr.title, DATA[mr_id]["title"]) + self.assertEqual( + marge_mr.web_url, + f"https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/{mr_id}" + ) + self.assertIsInstance(marge_mr.time_enqueued, timedelta)