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
229 lines
7.1 KiB
Python
229 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""
|
|
Unit tests for build_tools/packaging/linux/generate_package_indexes.py
|
|
|
|
USAGE:
|
|
pytest build_tools/packaging/tests/generate_package_indexes_test.py -v
|
|
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
THIS_DIR = Path(__file__).resolve().parent
|
|
LINUX_DIR = THIS_DIR.parent / "linux"
|
|
sys.path.insert(0, os.fspath(LINUX_DIR))
|
|
|
|
import generate_package_indexes as generate_package
|
|
|
|
|
|
class FakeS3:
|
|
"""Minimal fake for boto3 S3 client used by generate_*_from_s3.
|
|
|
|
Simulates:
|
|
- get_paginator("list_objects_v2").paginate(...)
|
|
- put_object(...)
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
list_pages_by_call: dict[tuple[str, str, str | None], list[dict[str, Any]]],
|
|
) -> None:
|
|
# dict[(op_name, Prefix, Delimiter)] -> list[page_dict]
|
|
self._list_pages_by_call = list_pages_by_call
|
|
self.put_calls: list[dict[str, str]] = []
|
|
|
|
def get_paginator(self, op_name: str) -> object:
|
|
assert op_name == "list_objects_v2"
|
|
s3 = self
|
|
|
|
class Dispatcher:
|
|
def paginate(self, **kwargs: Any):
|
|
prefix = kwargs.get("Prefix")
|
|
delim = kwargs.get("Delimiter", None)
|
|
key = (op_name, prefix, delim)
|
|
pages = s3._list_pages_by_call.get(key)
|
|
if pages is None:
|
|
raise AssertionError(f"No fake pages configured for call {key}")
|
|
for p in pages:
|
|
yield p
|
|
|
|
return Dispatcher()
|
|
|
|
def put_object(
|
|
self, Bucket: str, Key: str, Body: bytes | bytearray | str, ContentType: str
|
|
) -> None:
|
|
if isinstance(Body, (bytes, bytearray)):
|
|
body_text = Body.decode("utf-8")
|
|
else:
|
|
body_text = str(Body)
|
|
|
|
self.put_calls.append(
|
|
{
|
|
"Bucket": Bucket,
|
|
"Key": Key,
|
|
"Body": body_text,
|
|
"ContentType": ContentType,
|
|
}
|
|
)
|
|
|
|
def put_keys(self) -> list[str]:
|
|
return [c["Key"] for c in self.put_calls]
|
|
|
|
def put_body_for(self, key: str) -> str:
|
|
for c in self.put_calls:
|
|
if c["Key"] == key:
|
|
return c["Body"]
|
|
raise KeyError(key)
|
|
|
|
|
|
class GeneratePackageIndexesTest(unittest.TestCase):
|
|
def test_generate_index_from_s3_creates_indexes_and_links(self) -> None:
|
|
"""Ensure recursive index generation builds all directories and links correctly.
|
|
|
|
Verifies:
|
|
- indexes are created for root, architecture dir, and repodata dir
|
|
- files appear as links
|
|
- directories appear as folder links
|
|
- existing index.html objects are ignored
|
|
"""
|
|
bucket = "b"
|
|
prefix = "rpm/20260224-123"
|
|
|
|
pages: list[dict[str, Any]] = [
|
|
{
|
|
"Contents": [
|
|
{"Key": f"{prefix}/x86_64/a.rpm"},
|
|
{"Key": f"{prefix}/x86_64/b.rpm"},
|
|
{"Key": f"{prefix}/x86_64/repodata/repomd.xml"},
|
|
{"Key": f"{prefix}/x86_64/repodata/index.html"},
|
|
]
|
|
}
|
|
]
|
|
|
|
s3 = FakeS3(
|
|
list_pages_by_call={
|
|
("list_objects_v2", prefix, None): pages,
|
|
}
|
|
)
|
|
|
|
generate_package.generate_index_from_s3(s3, bucket, prefix)
|
|
|
|
self.assertIn(f"{prefix}/index.html", s3.put_keys())
|
|
self.assertIn(f"{prefix}/x86_64/index.html", s3.put_keys())
|
|
self.assertIn(f"{prefix}/x86_64/repodata/index.html", s3.put_keys())
|
|
|
|
root_html = s3.put_body_for(f"{prefix}/index.html")
|
|
self.assertIn('href="x86_64/index.html"', root_html)
|
|
|
|
x86_html = s3.put_body_for(f"{prefix}/x86_64/index.html")
|
|
self.assertIn('href="a.rpm"', x86_html)
|
|
self.assertIn('href="repodata/index.html"', x86_html)
|
|
|
|
repo_html = s3.put_body_for(f"{prefix}/x86_64/repodata/index.html")
|
|
self.assertIn('href="repomd.xml"', repo_html)
|
|
self.assertNotIn('href="index.html"', repo_html)
|
|
|
|
def test_generate_index_from_s3_respects_max_depth(self) -> None:
|
|
"""Verify max_depth limits recursion depth of generated indexes.
|
|
|
|
With max_depth=0:
|
|
- root index must exist
|
|
- first-level directory indexes allowed
|
|
- deeper nested directories must NOT get indexes
|
|
"""
|
|
bucket = "b"
|
|
prefix = "rpm/20260224-123"
|
|
|
|
pages: list[dict[str, Any]] = [
|
|
{
|
|
"Contents": [
|
|
{"Key": f"{prefix}/x86_64/a.rpm"},
|
|
{"Key": f"{prefix}/x86_64/repodata/repomd.xml"},
|
|
]
|
|
}
|
|
]
|
|
|
|
s3 = FakeS3(
|
|
list_pages_by_call={
|
|
("list_objects_v2", prefix, None): pages,
|
|
}
|
|
)
|
|
|
|
generate_package.generate_index_from_s3(s3, bucket, prefix, max_depth=0)
|
|
|
|
keys = s3.put_keys()
|
|
self.assertIn(f"{prefix}/index.html", keys)
|
|
self.assertIn(f"{prefix}/x86_64/index.html", keys)
|
|
self.assertNotIn(f"{prefix}/x86_64/repodata/index.html", keys)
|
|
|
|
def test_generate_top_index_from_s3_lists_subfolders(self) -> None:
|
|
"""Ensure top-level index lists child prefixes and files correctly.
|
|
|
|
Verifies:
|
|
- subfolders returned via CommonPrefixes are included
|
|
- top-level files are included
|
|
- index.html itself is excluded
|
|
"""
|
|
bucket = "b"
|
|
top_prefix = "rpm"
|
|
|
|
pages: list[dict[str, Any]] = [
|
|
{
|
|
"CommonPrefixes": [
|
|
{"Prefix": "rpm/20260224-111/"},
|
|
{"Prefix": "rpm/20260225-222/"},
|
|
],
|
|
"Contents": [
|
|
{"Key": "rpm/somefile.txt"},
|
|
{"Key": "rpm/index.html"},
|
|
],
|
|
}
|
|
]
|
|
|
|
s3 = FakeS3(
|
|
list_pages_by_call={
|
|
("list_objects_v2", f"{top_prefix}/", "/"): pages,
|
|
}
|
|
)
|
|
|
|
generate_package.generate_top_index_from_s3(s3, bucket, top_prefix)
|
|
|
|
self.assertIn(f"{top_prefix}/index.html", s3.put_keys())
|
|
html = s3.put_body_for(f"{top_prefix}/index.html")
|
|
|
|
self.assertIn('href="20260224-111/index.html"', html)
|
|
self.assertIn('href="somefile.txt"', html)
|
|
self.assertNotIn('href="index.html"', html)
|
|
|
|
def test_generate_index_html_skips_dotfiles(self) -> None:
|
|
"""Ensure local filesystem index generation ignores dotfiles.
|
|
|
|
Verifies:
|
|
- visible files appear in the index
|
|
- hidden files (starting with '.') are excluded
|
|
"""
|
|
with tempfile.TemporaryDirectory() as td:
|
|
d = Path(td) / "repo"
|
|
d.mkdir(parents=True, exist_ok=True)
|
|
|
|
(d / ".hidden").write_text("x", encoding="utf-8")
|
|
(d / "visible").write_text("y", encoding="utf-8")
|
|
|
|
generate_package.generate_index_html(str(d))
|
|
|
|
html = (d / "index.html").read_text(encoding="utf-8")
|
|
self.assertIn("visible", html)
|
|
self.assertNotIn(".hidden", html)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|