Files
TheRock/build_tools/tests/find_latest_artifacts_test.py
T
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

238 lines
9.4 KiB
Python

# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
import os
from pathlib import Path
import sys
import unittest
from unittest import mock
sys.path.insert(0, os.fspath(Path(__file__).parent.parent))
from find_latest_artifacts import find_latest_artifacts
from github_actions.github_actions_api import (
GitHubAPIError,
is_authenticated_github_api_available,
)
def _skip_unless_authenticated_github_api_is_available(test_func):
"""Decorator to skip tests unless GitHub API is available."""
return unittest.skipUnless(
is_authenticated_github_api_available(),
"No authenticated GitHub API available (need GITHUB_TOKEN or authenticated gh CLI)",
)(test_func)
# --- Mocking strategy ---
#
# These tests mock two layers:
#
# 1. gha_query_recent_branch_commits() — Mocked to return a fixed list of
# known commit SHAs. This avoids dependence on the evolving tip of any
# branch and lets us control which commits are "searched".
#
# 2. check_if_artifacts_exist() — Mocked because S3 artifacts are subject to
# a retention policy and may be deleted for older runs. By controlling this
# mock's return value per-commit, we can test commits/workflows that have
# already expired and we can also simulate missing artifacts due to failed
# builds.
#
# The GitHub API calls within find_artifacts_for_commit() (querying workflow
# runs by commit SHA, retrieving bucket info) are NOT mocked — they hit the
# real API. The pinned commits below have stable workflow run history that is
# unlikely to change. If tests become brittle, we can re-evaluate.
# Two consecutive commits on TheRock main with CI workflow runs, simulating
# what gha_query_recent_branch_commits() would return (most recent first).
#
# https://github.com/ROCm/TheRock/commit/5ea91c38d19237716ba0c9382928da12a6fc9b08
# CI run: https://github.com/ROCm/TheRock/actions/runs/21249928112
TEST_THEROCK_COMMIT_NEWER = "5ea91c38d19237716ba0c9382928da12a6fc9b08"
# https://github.com/ROCm/TheRock/commit/02946b2295f8fae31fd506c1be6735b5911cdc6b
# CI run: https://github.com/ROCm/TheRock/actions/runs/21243829022
TEST_THEROCK_COMMIT_OLDER = "02946b2295f8fae31fd506c1be6735b5911cdc6b"
class FindLatestArtifactsTest(unittest.TestCase):
"""Tests for find_latest_artifacts() with real GitHub API calls."""
@_skip_unless_authenticated_github_api_is_available
@mock.patch("find_artifacts_for_commit.check_if_artifacts_exist", return_value=True)
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_returns_first_commit_with_artifacts(self, mock_commits, mock_check):
"""Returns the first commit that has artifacts."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
results = find_latest_artifacts(
artifact_groups=["gfx110X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNotNone(results)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].git_commit_sha, TEST_THEROCK_COMMIT_NEWER)
@_skip_unless_authenticated_github_api_is_available
@mock.patch("find_artifacts_for_commit.check_if_artifacts_exist")
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_skips_commits_missing_artifacts(self, mock_commits, mock_check):
"""Skips commits whose artifacts are missing (e.g. flaky build)."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
# First commit's artifacts are missing, second commit's are present
def check_by_commit(info):
return info.git_commit_sha != TEST_THEROCK_COMMIT_NEWER
mock_check.side_effect = check_by_commit
results = find_latest_artifacts(
artifact_groups=["gfx110X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNotNone(results)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].git_commit_sha, TEST_THEROCK_COMMIT_OLDER)
@_skip_unless_authenticated_github_api_is_available
@mock.patch(
"find_artifacts_for_commit.check_if_artifacts_exist", return_value=False
)
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_returns_none_when_all_artifacts_missing(self, mock_commits, mock_check):
"""Returns None when no commits have artifacts available."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
results = find_latest_artifacts(
artifact_groups=["gfx110X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNone(results)
def test_rate_limit_error_on_commit_list_raises_exception(self):
"""Rate limit when fetching commits raises GitHubAPIError."""
rate_limit_error = GitHubAPIError(
"GitHub API rate limit exceeded. "
"Authenticate with `gh auth login` or set GITHUB_TOKEN to increase limits."
)
with mock.patch(
"find_latest_artifacts.gha_query_recent_branch_commits",
side_effect=rate_limit_error,
):
with self.assertRaises(GitHubAPIError) as ctx:
find_latest_artifacts(
artifact_groups=["gfx110X-all"],
github_repository_name="ROCm/TheRock",
)
self.assertIn("rate limit", str(ctx.exception).lower())
class FindLatestArtifactsMultiGroupTest(unittest.TestCase):
"""Tests for multi-group behavior of find_latest_artifacts()."""
@_skip_unless_authenticated_github_api_is_available
@mock.patch("find_artifacts_for_commit.check_if_artifacts_exist", return_value=True)
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_multiple_groups_all_found(self, mock_commits, mock_check):
"""Returns results when all requested groups have artifacts."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
results = find_latest_artifacts(
artifact_groups=["gfx110X-all", "gfx120X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNotNone(results)
self.assertEqual(len(results), 2)
self.assertEqual(results[0].artifact_group, "gfx110X-all")
self.assertEqual(results[1].artifact_group, "gfx120X-all")
# Both from the same commit
self.assertEqual(results[0].git_commit_sha, results[1].git_commit_sha)
@_skip_unless_authenticated_github_api_is_available
@mock.patch("find_artifacts_for_commit.check_if_artifacts_exist")
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_skips_commit_with_partial_groups(self, mock_commits, mock_check):
"""Skips a commit where only some groups have artifacts, returns next."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
# Newer commit: only gfx110X has artifacts
# Older commit: both groups have artifacts
def check_by_commit_and_group(info):
if info.git_commit_sha == TEST_THEROCK_COMMIT_NEWER:
return info.artifact_group == "gfx110X-all"
return True # older commit has everything
mock_check.side_effect = check_by_commit_and_group
results = find_latest_artifacts(
artifact_groups=["gfx110X-all", "gfx120X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNotNone(results)
self.assertEqual(len(results), 2)
# Should come from the older commit (newer was partial)
self.assertEqual(results[0].git_commit_sha, TEST_THEROCK_COMMIT_OLDER)
self.assertEqual(results[1].git_commit_sha, TEST_THEROCK_COMMIT_OLDER)
@_skip_unless_authenticated_github_api_is_available
@mock.patch("find_artifacts_for_commit.check_if_artifacts_exist")
@mock.patch("find_latest_artifacts.gha_query_recent_branch_commits")
def test_returns_none_when_no_commit_has_all_groups(self, mock_commits, mock_check):
"""Returns None when no single commit has all requested groups."""
mock_commits.return_value = [
TEST_THEROCK_COMMIT_NEWER,
TEST_THEROCK_COMMIT_OLDER,
]
# Each commit only has one of the two groups
def check_alternating(info):
if info.git_commit_sha == TEST_THEROCK_COMMIT_NEWER:
return info.artifact_group == "gfx110X-all"
return info.artifact_group == "gfx120X-all"
mock_check.side_effect = check_alternating
results = find_latest_artifacts(
artifact_groups=["gfx110X-all", "gfx120X-all"],
github_repository_name="ROCm/TheRock",
workflow_file_name="ci.yml",
platform="linux",
)
self.assertIsNone(results)
if __name__ == "__main__":
unittest.main()