Files
TheRock/rocm-systems/.github/scripts/test-rocjitsu-corpus-translation-comment.py
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

345 lines
12 KiB
Python

#!/usr/bin/env python3
# Copyright (c) 2026 Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
from pathlib import Path
import tempfile
import unittest
from unittest import mock
import rocjitsu_corpus_translation_comment as HELPER
REPOSITORY = "ROCm/rocm-systems"
HEAD_SHA = "a" * 40
NEW_HEAD_SHA = "b" * 40
class FakeApi:
def __init__(self) -> None:
self.pull = {
"base": {"ref": "develop", "repo": {"full_name": REPOSITORY}},
"head": {
"repo": {"full_name": REPOSITORY},
"sha": HEAD_SHA,
},
}
self.runs = [{"conclusion": "success", "event": "push", "id": 200}]
self.artifacts = {
100: [self._artifact(HELPER.CANDIDATE_ARTIFACT)],
200: [self._artifact(HELPER.BASELINE_ARTIFACT)],
}
self.comments = []
self.calls = []
def get(self, path):
if path == f"/repos/{REPOSITORY}/pulls/7":
return self.pull
raise AssertionError(path)
def paginate(self, path, key=None):
if path.endswith(f"/commits/{HEAD_SHA}/pulls"):
return []
if path.endswith("/runs?branch=develop&status=completed"):
self.assert_key(key, "workflow_runs")
return self.runs
if path.endswith("/comments"):
self.assert_key(key, None)
return self.comments
if "/actions/runs/" in path and path.endswith("/artifacts"):
self.assert_key(key, "artifacts")
run_id = int(path.split("/actions/runs/")[1].split("/")[0])
return self.artifacts.get(run_id, [])
raise AssertionError(path)
def create(self, path, payload):
self.calls.append(("create", path, payload))
def update(self, path, payload):
self.calls.append(("update", path, payload))
def delete(self, path):
self.calls.append(("delete", path, None))
@staticmethod
def _artifact(name):
return {"expired": False, "name": name, "size_in_bytes": 5_000_000}
@staticmethod
def assert_key(actual, expected):
if actual != expected:
raise AssertionError((actual, expected))
class PlanTest(unittest.TestCase):
def setUp(self) -> None:
self.api = FakeApi()
self.event = {
"workflow_run": {
"event": "pull_request",
"conclusion": "success",
"head_repository": {"full_name": REPOSITORY},
"head_sha": HEAD_SHA,
"id": 100,
"path": HELPER.TRUSTED_WORKFLOW_PATH,
"pull_requests": [{"number": 7}],
}
}
def test_resolves_candidate_and_latest_baseline(self) -> None:
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertTrue(plan.ready)
self.assertEqual(plan.pull_number, 7)
self.assertEqual(plan.candidate_run_id, 100)
self.assertEqual(plan.baseline_run_id, 200)
self.assertEqual(plan.candidate_head_sha, HEAD_SHA)
def test_allows_bootstrap_without_baseline(self) -> None:
self.api.runs = []
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertTrue(plan.ready)
self.assertIsNone(plan.baseline_run_id)
def test_accepts_failed_source_run_with_valid_candidate(self) -> None:
self.event["workflow_run"]["conclusion"] = "failure"
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertTrue(plan.ready)
self.assertEqual(plan.candidate_run_id, 100)
def test_skips_source_run_without_candidate(self) -> None:
self.event["workflow_run"]["conclusion"] = "failure"
self.api.artifacts[100] = []
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("found 0", plan.reason)
def test_skips_untrusted_source_workflow_path(self) -> None:
self.event["workflow_run"]["path"] = ".github/workflows/untrusted.yml"
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("workflow path", plan.reason)
def test_skips_source_run_from_fork(self) -> None:
self.event["workflow_run"]["head_repository"]["full_name"] = "someone/fork"
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("did not run from", plan.reason)
def test_skips_stale_pull_request_revision(self) -> None:
self.api.pull["head"]["sha"] = NEW_HEAD_SHA
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("does not match", plan.reason)
def test_skips_pull_request_from_fork(self) -> None:
self.api.pull["head"]["repo"]["full_name"] = "someone/fork"
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("same-repository", plan.reason)
def test_ignores_unsuccessful_baseline_run(self) -> None:
self.api.runs.insert(0, {"conclusion": "failure", "event": "push", "id": 201})
self.api.artifacts[201] = [self.api._artifact(HELPER.BASELINE_ARTIFACT)]
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertEqual(plan.baseline_run_id, 200)
def test_skips_pull_request_with_different_base(self) -> None:
self.api.pull["base"]["ref"] = "release/7.0"
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
self.assertFalse(plan.ready)
self.assertIn("does not target", plan.reason)
def test_rejects_oversized_candidate(self) -> None:
self.api.artifacts[100][0]["size_in_bytes"] = HELPER.MAXIMUM_ARTIFACT_BYTES + 1
with self.assertRaisesRegex(ValueError, "candidate artifact"):
HELPER.resolve_plan(self.event, self.api, REPOSITORY)
def test_writes_canonical_outputs(self) -> None:
plan = HELPER.resolve_plan(self.event, self.api, REPOSITORY)
with tempfile.TemporaryDirectory() as temporary_directory:
output = Path(temporary_directory) / "outputs"
HELPER.write_plan_outputs(output, plan)
self.assertEqual(
output.read_text(encoding="utf-8"),
"ready=true\n"
"pull-number=7\n"
"candidate-run-id=100\n"
f"candidate-head-sha={HEAD_SHA}\n"
"baseline-found=true\n"
"baseline-run-id=200\n",
)
class GitHubApiTest(unittest.TestCase):
def test_paginate_rejects_non_object_keyed_response(self) -> None:
api = HELPER.GitHubApi(REPOSITORY, "token", "https://api.github.com")
with mock.patch.object(api, "get", return_value=[]):
with self.assertRaisesRegex(ValueError, "is not an object"):
api.paginate("/artifacts", "artifacts")
class CommentTest(unittest.TestCase):
def setUp(self) -> None:
self.api = FakeApi()
def test_renders_warning_and_resolution(self) -> None:
warning = HELPER.render_comment(
REPOSITORY,
100,
HEAD_SHA,
True,
2,
"## report\n\n> [!WARNING]\n> changed\n",
"https://github.com",
)
self.assertIn(HELPER.COMMENT_MARKER, warning)
self.assertIn("[!WARNING]", warning)
self.assertIn(HEAD_SHA, warning)
self.assertIsNone(
HELPER.render_comment(
REPOSITORY,
100,
HEAD_SHA,
True,
0,
"## clean",
"https://github.com",
)
)
def test_renders_missing_baseline_warning(self) -> None:
body = HELPER.render_comment(
REPOSITORY, 100, HEAD_SHA, False, None, None, "https://github.com"
)
self.assertIn("No canonical develop baseline", body)
def test_creates_updates_and_removes_sticky_comment(self) -> None:
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, "warning")
self.assertEqual(self.api.calls[0][0], "create")
self.api.calls.clear()
self.api.comments = [
{
"body": HELPER.COMMENT_MARKER,
"id": 41,
"user": {"login": "github-actions[bot]"},
},
{
"body": HELPER.COMMENT_MARKER,
"id": 42,
"user": {"login": "github-actions[bot]"},
},
]
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, "new warning")
self.assertEqual([call[0] for call in self.api.calls], ["update", "delete"])
self.api.calls.clear()
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, None)
self.assertEqual([call[0] for call in self.api.calls], ["delete", "delete"])
def test_ignores_comment_with_null_body(self) -> None:
self.api.comments = [
{
"body": None,
"id": 41,
"user": {"login": "github-actions[bot]"},
}
]
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, "warning")
self.assertEqual([call[0] for call in self.api.calls], ["create"])
def test_stale_head_cannot_create_update_or_delete_comment(self) -> None:
existing = {
"body": HELPER.COMMENT_MARKER,
"id": 41,
"user": {"login": "github-actions[bot]"},
}
for body, comments in (
("warning", []),
("new warning", [existing]),
(None, [existing]),
):
with self.subTest(body=body):
self.api.calls.clear()
self.api.comments = comments
self.api.pull["head"]["sha"] = NEW_HEAD_SHA
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, body)
self.assertEqual(self.api.calls, [])
def test_rechecks_head_before_removing_duplicate_after_update(self) -> None:
self.api.comments = [
{
"body": HELPER.COMMENT_MARKER,
"id": 41,
"user": {"login": "github-actions[bot]"},
},
{
"body": HELPER.COMMENT_MARKER,
"id": 42,
"user": {"login": "github-actions[bot]"},
},
]
current_pull = self.api.pull
stale_pull = {
**self.api.pull,
"head": {**self.api.pull["head"], "sha": NEW_HEAD_SHA},
}
with mock.patch.object(self.api, "get", side_effect=[current_pull, stale_pull]):
HELPER.synchronize_comment(self.api, REPOSITORY, 7, HEAD_SHA, "new warning")
self.assertEqual([call[0] for call in self.api.calls], ["update"])
def test_comparison_timeout_is_reported(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
directory = Path(temporary_directory)
baseline = directory / "baseline.json"
candidate = directory / "candidate.json"
baseline.write_text("{}", encoding="utf-8")
candidate.write_text("{}", encoding="utf-8")
timeout = HELPER.subprocess.TimeoutExpired(
"compare", HELPER.COMPARISON_TIMEOUT_SECONDS
)
with mock.patch.object(
HELPER.subprocess, "run", side_effect=timeout
) as run:
with self.assertRaisesRegex(ValueError, "exceeded 60 seconds"):
HELPER.compare_candidate(
Path(__file__), baseline, candidate, baseline_found=True
)
self.assertEqual(
run.call_args.kwargs["timeout"], HELPER.COMPARISON_TIMEOUT_SECONDS
)
if __name__ == "__main__":
unittest.main()