Files
TheRock/build_tools/github_actions/tests/upload_python_packages_test.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

242 lines
9.8 KiB
Python

#!/usr/bin/env python
"""Unit tests for upload_python_packages.py.
Tests verify that upload functions pass correct StorageLocations to the
StorageBackend, producing the expected file layout. Uses LocalStorageBackend
with a temp directory so no mocking of subprocess or boto3 is needed.
"""
import os
import sys
import tempfile
import unittest
import unittest.mock
from pathlib import Path
# Add build_tools to path so _therock_utils is importable.
sys.path.insert(0, os.fspath(Path(__file__).parent.parent.parent))
# Add github_actions to path so upload_python_packages is importable.
sys.path.insert(0, os.fspath(Path(__file__).parent.parent))
from _therock_utils.workflow_outputs import WorkflowOutputRoot
from _therock_utils.storage_backend import LocalStorageBackend
import upload_python_packages
def _make_output_root(
run_id="12345",
platform="linux",
bucket="therock-ci-artifacts",
external_repo="",
):
return WorkflowOutputRoot(
bucket=bucket,
external_repo=external_repo,
run_id=run_id,
platform=platform,
)
class TestFindPackageFiles(unittest.TestCase):
"""Tests for find_package_files()."""
def test_finds_wheels_sdists_and_index(self):
with tempfile.TemporaryDirectory() as tmp:
dist_dir = Path(tmp)
(dist_dir / "rocm-1.0.whl").write_bytes(b"whl")
(dist_dir / "rocm-1.0.tar.gz").write_bytes(b"sdist")
(dist_dir / "index.html").write_text("<html></html>")
(dist_dir / "unrelated.txt").write_text("ignore")
files = upload_python_packages.find_package_files(dist_dir)
names = sorted(f.name for f in files)
self.assertEqual(names, ["index.html", "rocm-1.0.tar.gz", "rocm-1.0.whl"])
def test_empty_directory(self):
with tempfile.TemporaryDirectory() as tmp:
files = upload_python_packages.find_package_files(Path(tmp))
self.assertEqual(files, [])
class TestUploadPackages(unittest.TestCase):
"""Tests for upload_packages()."""
def test_uploads_package_files(self):
output_root = _make_output_root()
packages_loc = output_root.python_packages("gfx94X-dcgpu")
with tempfile.TemporaryDirectory() as tmp, tempfile.TemporaryDirectory() as staging:
dist_dir = Path(tmp)
staging_dir = Path(staging)
(dist_dir / "rocm-1.0.whl").write_bytes(b"whl")
(dist_dir / "rocm-1.0.tar.gz").write_bytes(b"sdist")
(dist_dir / "index.html").write_text("<html></html>")
backend = LocalStorageBackend(staging_dir)
upload_python_packages.upload_packages(dist_dir, packages_loc, backend)
base = staging_dir / "12345-linux" / "python" / "gfx94X-dcgpu"
self.assertTrue((base / "rocm-1.0.whl").is_file())
self.assertTrue((base / "rocm-1.0.tar.gz").is_file())
self.assertTrue((base / "index.html").is_file())
def test_no_files_raises(self):
output_root = _make_output_root()
packages_loc = output_root.python_packages("gfx94X-dcgpu")
with tempfile.TemporaryDirectory() as tmp, tempfile.TemporaryDirectory() as staging:
dist_dir = Path(tmp)
staging_dir = Path(staging)
backend = LocalStorageBackend(staging_dir)
with self.assertRaises(FileNotFoundError):
upload_python_packages.upload_packages(dist_dir, packages_loc, backend)
def test_external_repo_prefix(self):
output_root = _make_output_root(
external_repo="Fork-TheRock/",
bucket="therock-ci-artifacts-external",
)
packages_loc = output_root.python_packages("gfx94X-dcgpu")
with tempfile.TemporaryDirectory() as tmp, tempfile.TemporaryDirectory() as staging:
dist_dir = Path(tmp)
staging_dir = Path(staging)
(dist_dir / "rocm-1.0.whl").write_bytes(b"whl")
backend = LocalStorageBackend(staging_dir)
upload_python_packages.upload_packages(dist_dir, packages_loc, backend)
self.assertTrue(
(
staging_dir
/ "Fork-TheRock"
/ "12345-linux"
/ "python"
/ "gfx94X-dcgpu"
/ "rocm-1.0.whl"
).is_file()
)
class TestMultiArchUploadPath(unittest.TestCase):
"""Tests that multi-arch uploads go to python/ without an artifact_group subdir."""
def test_multiarch_upload_path(self):
output_root = _make_output_root()
packages_loc = output_root.python_packages("")
with tempfile.TemporaryDirectory() as tmp, tempfile.TemporaryDirectory() as staging:
dist_dir = Path(tmp)
staging_dir = Path(staging)
(dist_dir / "rocm-1.0.whl").write_bytes(b"whl")
(dist_dir / "index.html").write_text("<html></html>")
backend = LocalStorageBackend(staging_dir)
upload_python_packages.upload_packages(dist_dir, packages_loc, backend)
base = staging_dir / "12345-linux" / "python"
self.assertTrue((base / "rocm-1.0.whl").is_file())
self.assertTrue((base / "index.html").is_file())
# Confirm no artifact_group subdirectory was created
self.assertFalse((base / "multi-arch-release").exists())
class TestGenerateIndex(unittest.TestCase):
"""Tests for generate_index() flat vs per-family auto-detection."""
def test_flat_layout_skips_client_side_index(self):
"""Flat dist/ (no subdirs) skips client index generation — the
therock-ci-artifacts bucket serves index.html from its own side,
and a client-generated index would race with the server view
when multiple jobs append to the same prefix."""
with tempfile.TemporaryDirectory() as tmp:
dist_dir = Path(tmp)
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
(dist_dir / "rocm_sdk_device_gfx942-1.0.whl").write_bytes(b"device")
upload_python_packages.generate_index(dist_dir, multiarch=True)
self.assertFalse((dist_dir / "index.html").exists())
self.assertFalse(any(d.is_dir() for d in dist_dir.iterdir()))
def test_per_family_layout_creates_family_indexes(self):
"""Per-family dist/ (with subdirs) creates per-family indexes, not top-level."""
with tempfile.TemporaryDirectory() as tmp:
dist_dir = Path(tmp)
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
(dist_dir / "gfx94X-dcgpu").mkdir()
(
dist_dir / "gfx94X-dcgpu" / "rocm_sdk_libraries_gfx94x_dcgpu-1.0.whl"
).write_bytes(b"libs")
upload_python_packages.generate_index(dist_dir, multiarch=True)
self.assertTrue((dist_dir / "gfx94X-dcgpu" / "index.html").is_file())
self.assertFalse((dist_dir / "index.html").is_file())
def test_dot_dir_treated_as_flat(self):
"""A dot-directory (e.g. .kpack_staging) is a staging artifact, not a
GPU family, so it must not flip detection into per-family mode."""
with tempfile.TemporaryDirectory() as tmp:
dist_dir = Path(tmp)
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
(dist_dir / ".kpack_staging").mkdir()
(dist_dir / ".kpack_staging" / "scratch.whl").write_bytes(b"scratch")
self.assertEqual(upload_python_packages.detect_family_subdirs(dist_dir), [])
upload_python_packages.generate_index(dist_dir, multiarch=True)
# Flat (kpack-split) handling: no per-family index generated.
self.assertFalse((dist_dir / ".kpack_staging" / "index.html").exists())
def test_dry_run_creates_nothing(self):
"""dry_run=True creates no files regardless of layout."""
with tempfile.TemporaryDirectory() as tmp:
dist_dir = Path(tmp)
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
upload_python_packages.generate_index(
dist_dir, multiarch=True, dry_run=True
)
self.assertFalse((dist_dir / "index.html").is_file())
class TestWriteGhaUploadSummary(unittest.TestCase):
"""Tests for write_gha_upload_summary() three-way families branch."""
def _make_loc(self):
return _make_output_root().python_packages("")
@unittest.mock.patch("upload_python_packages.gha_append_step_summary")
def test_none_families_single_arch(self, mock_summary):
"""families=None → single-arch summary with /index.html URL."""
upload_python_packages.write_gha_upload_summary(self._make_loc(), families=None)
text = mock_summary.call_args[0][0]
self.assertIn("/index.html", text)
self.assertNotIn("kpack-split", text)
self.assertNotIn("per-family", text.lower())
@unittest.mock.patch("upload_python_packages.gha_append_step_summary")
def test_empty_families_kpack_split(self, mock_summary):
"""families=[] → kpack-split summary with /index.html URL."""
upload_python_packages.write_gha_upload_summary(self._make_loc(), families=[])
text = mock_summary.call_args[0][0]
self.assertIn("kpack-split", text)
self.assertIn("/index.html", text)
@unittest.mock.patch("upload_python_packages.gha_append_step_summary")
def test_nonempty_families_per_family(self, mock_summary):
"""families=[...] → per-family summary with per-family index URLs."""
upload_python_packages.write_gha_upload_summary(
self._make_loc(), families=["gfx94X-dcgpu", "gfx120X-all"]
)
text = mock_summary.call_args[0][0]
self.assertIn("gfx94X-dcgpu/index.html", text)
self.assertIn("gfx120X-all/index.html", text)
self.assertNotIn("kpack-split", text)
if __name__ == "__main__":
unittest.main()