blob: 84d6da0f2600aa50fcfadfc807f44346735d99ea [file] [log] [blame]
from contextlib import suppress
from datetime import datetime, timedelta
from unittest import mock
from unittest.mock import MagicMock, patch
import ci_post_gantt
import pytest
from ci_gantt_chart import generate_gantt_chart
from ci_post_gantt import Gitlab, MockGanttExit
def create_mock_job(
name, id, status, created_at, queued_duration, started_at, finished_at=None
):
mock_job = MagicMock()
mock_job.name = name
mock_job.status = status
mock_job.id = id
mock_job.created_at = created_at
mock_job.queued_duration = queued_duration
mock_job.started_at = started_at
mock_job.finished_at = finished_at
return mock_job
@pytest.fixture
def fake_pipeline():
current_time = datetime.fromisoformat("2024-12-17 23:54:13.940091+00:00")
created_at = current_time - timedelta(minutes=10)
job1 = create_mock_job(
name="job1",
id="1",
status="success",
created_at=created_at.isoformat(),
queued_duration=1, # seconds
started_at=(created_at + timedelta(seconds=2)).isoformat(),
finished_at=(created_at + timedelta(minutes=1)).isoformat(),
)
mock_pipeline = MagicMock()
mock_pipeline.web_url = "https://gitlab.freedesktop.org/mesa/mesa/-/pipelines/9999"
mock_pipeline.duration = 600 # Total pipeline duration in seconds
mock_pipeline.created_at = created_at.isoformat()
mock_pipeline.yaml_errors = False
mock_pipeline.jobs.list.return_value = [job1]
return mock_pipeline
def test_generate_gantt_chart(fake_pipeline):
fig = generate_gantt_chart(fake_pipeline)
fig_dict = fig.to_dict()
assert "data" in fig_dict
# Extract all job names from the "y" axis in the Gantt chart data
all_job_names = set()
for trace in fig_dict["data"]:
if "y" in trace:
all_job_names.update(trace["y"])
assert any(
"job1" in job for job in all_job_names
), "job1 should be present in the Gantt chart"
def test_ci_timeout(fake_pipeline):
fig = generate_gantt_chart(fake_pipeline, ci_timeout=1)
fig_dict = fig.to_dict()
timeout_line = None
for shape in fig_dict.get("layout", {}).get("shapes", []):
if shape.get("line", {}).get("dash") == "dash":
timeout_line = shape
break
assert timeout_line is not None, "Timeout line should exist in the Gantt chart"
timeout_x = timeout_line["x0"]
# Check that the timeout line is 1 minute after the pipeline creation time
pipeline_created_at = datetime.fromisoformat(fake_pipeline.created_at)
expected_timeout = pipeline_created_at + timedelta(minutes=1)
assert (
timeout_x == expected_timeout
), f"Timeout should be at {expected_timeout}, got {timeout_x}"
def test_marge_bot_user_id():
with patch("ci_post_gantt.Gitlab") as MockGitlab:
mock_gitlab_instance = MagicMock(spec=Gitlab)
mock_gitlab_instance.users = MagicMock()
MockGitlab.return_value = mock_gitlab_instance
marge_bot_user_id = 12345
ci_post_gantt.main("fake_token", None, marge_bot_user_id)
mock_gitlab_instance.users.get.assert_called_once_with(marge_bot_user_id)
def test_project_ids():
current_time = datetime.now()
project_id_1 = 176
event_1 = MagicMock()
event_1.project_id = project_id_1
event_1.created_at = (current_time - timedelta(days=1)).isoformat()
event_1.note = {"body": f"Event for project {project_id_1}"}
project_id_2 = 166
event_2 = MagicMock()
event_2.project_id = project_id_2
event_2.created_at = (current_time - timedelta(days=2)).isoformat()
event_2.note = {"body": f"Event for project {project_id_2}"}
with patch("ci_post_gantt.Gitlab") as MockGitlab:
mock_user = MagicMock()
mock_user.events = MagicMock()
mock_user.events.list.return_value = [event_1, event_2]
mock_gitlab_instance = MagicMock(spec=Gitlab)
mock_gitlab_instance.users = MagicMock()
mock_gitlab_instance.users.get.return_value = mock_user
MockGitlab.return_value = mock_gitlab_instance
last_event_date = (current_time - timedelta(days=3)).isoformat()
# Test a single project id
ci_post_gantt.main("fake_token", last_event_date)
marge_bot_single_project_scope = [
event.note["body"]
for event in mock_user.events.list.return_value
if event.project_id == project_id_1
]
assert f"Event for project {project_id_1}" in marge_bot_single_project_scope
assert f"Event for project {project_id_2}" not in marge_bot_single_project_scope
# Test multiple project ids
ci_post_gantt.main(
"fake_token", last_event_date, 9716, [project_id_1, project_id_2]
)
marge_bot_multiple_project_scope = [
event.note["body"] for event in mock_user.events.list.return_value
]
assert f"Event for project {project_id_1}" in marge_bot_multiple_project_scope
assert f"Event for project {project_id_2}" in marge_bot_multiple_project_scope
def test_add_gantt_after_pipeline_message():
current_time = datetime.now()
plain_url = "https://gitlab.freedesktop.org/mesa/mesa/-/pipelines/12345"
plain_message = (
f"I couldn't merge this branch: CI failed! See pipeline {plain_url}."
)
event_plain = MagicMock()
event_plain.project_id = 176
event_plain.created_at = (current_time - timedelta(days=1)).isoformat()
event_plain.note = {"body": plain_message}
summary_url = "https://gitlab.freedesktop.org/mesa/mesa/-/pipelines/99999"
summary_message = (
"I couldn't merge this branch: "
f"CI failed! See pipeline {summary_url}.<br>There were problems with job:"
"[lavapipe](https://gitlab.freedesktop.org/mesa/mesa/-/jobs/68141218)<details><summary> "
"3 crashed tests</summary>dEQP-VK.ray_query.builtin.instancecustomindex.frag.aabbs,Crash<br>dEQP"
"-VK.ray_query.builtin.objecttoworld.frag.aabbs,Crash<br>dEQP-VK.sparse_resources.shader_intrinsics."
"2d_array_sparse_fetch.g16_b16r16_2plane_444_unorm.11_37_3_nontemporal,Crash<br></details>"
)
event_with_summary = MagicMock()
event_with_summary.project_id = 176
event_with_summary.created_at = (current_time - timedelta(days=1)).isoformat()
event_with_summary.note = {"body": summary_message}
with patch("ci_post_gantt.Gitlab") as MockGitlab, patch(
"ci_post_gantt.get_gitlab_pipeline_from_url", return_value=None
) as mock_get_gitlab_pipeline_from_url:
def safe_mock(*args, **kwargs):
with suppress(TypeError):
raise MockGanttExit("Exiting for test purposes")
mock_get_gitlab_pipeline_from_url.side_effect = safe_mock
mock_user = MagicMock()
mock_user.events = MagicMock()
mock_user.events.list.return_value = [event_plain, event_with_summary]
mock_gitlab_instance = MagicMock(spec=Gitlab)
mock_gitlab_instance.users = MagicMock()
mock_gitlab_instance.users.get.return_value = mock_user
MockGitlab.return_value = mock_gitlab_instance
last_event_date = (current_time - timedelta(days=3)).isoformat()
ci_post_gantt.main("fake_token", last_event_date, 12345)
mock_get_gitlab_pipeline_from_url.assert_has_calls(
[
mock.call(mock_gitlab_instance, plain_url),
mock.call(mock_gitlab_instance, summary_url),
],
any_order=True,
)