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
431 lines
17 KiB
Python
431 lines
17 KiB
Python
#!/usr/bin/env python
|
|
# Copyright Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Unit tests for generate_local_index.py.
|
|
|
|
Tests verify that index generation creates correct HTML structure with proper
|
|
relative paths for local and parent files, and that multi-arch mode correctly
|
|
processes directory structures.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from contextlib import redirect_stderr
|
|
from io import StringIO
|
|
from pathlib import Path
|
|
|
|
# Add build_tools/packaging/python to path so generate_local_index is importable.
|
|
sys.path.insert(0, os.fspath(Path(__file__).parent.parent / "python"))
|
|
|
|
from generate_local_index import (
|
|
generate_multiarch_indexes,
|
|
generate_simple_index,
|
|
main,
|
|
)
|
|
|
|
|
|
class TestGenerateSimpleIndex(unittest.TestCase):
|
|
"""Tests for generate_simple_index()."""
|
|
|
|
def test_local_files_only(self):
|
|
"""Test index with only local files."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
local_files = [
|
|
Path(tmp) / "package1-1.0.whl",
|
|
Path(tmp) / "package2-2.0.tar.gz",
|
|
]
|
|
|
|
generate_simple_index(output, local_files)
|
|
|
|
content = output.read_text()
|
|
self.assertIn("<!DOCTYPE html>", content)
|
|
self.assertIn("<title>Package Index</title>", content)
|
|
self.assertIn('<a href="./package1-1.0.whl">package1-1.0.whl</a>', content)
|
|
self.assertIn(
|
|
'<a href="./package2-2.0.tar.gz">package2-2.0.tar.gz</a>', content
|
|
)
|
|
|
|
def test_parent_files_only(self):
|
|
"""Test index with only parent files."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
parent_files = [
|
|
Path(tmp) / "parent1-1.0.whl",
|
|
Path(tmp) / "parent2-2.0.tar.gz",
|
|
]
|
|
|
|
generate_simple_index(output, [], parent_files=parent_files)
|
|
|
|
content = output.read_text()
|
|
self.assertIn('<a href="../parent1-1.0.whl">parent1-1.0.whl</a>', content)
|
|
self.assertIn(
|
|
'<a href="../parent2-2.0.tar.gz">parent2-2.0.tar.gz</a>', content
|
|
)
|
|
|
|
def test_mixed_local_and_parent_files(self):
|
|
"""Test index with both local and parent files."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
local_files = [Path(tmp) / "local-1.0.whl"]
|
|
parent_files = [Path(tmp) / "parent-1.0.whl"]
|
|
|
|
generate_simple_index(output, local_files, parent_files=parent_files)
|
|
|
|
content = output.read_text()
|
|
self.assertIn('<a href="./local-1.0.whl">local-1.0.whl</a>', content)
|
|
self.assertIn('<a href="../parent-1.0.whl">parent-1.0.whl</a>', content)
|
|
|
|
def test_custom_title(self):
|
|
"""Test index with custom title."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
generate_simple_index(output, [], title="Custom Title")
|
|
|
|
content = output.read_text()
|
|
self.assertIn("<title>Custom Title</title>", content)
|
|
self.assertIn("<h1>Custom Title</h1>", content)
|
|
|
|
def test_empty_lists(self):
|
|
"""Test index with no files."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
generate_simple_index(output, [])
|
|
|
|
content = output.read_text()
|
|
self.assertIn("<!DOCTYPE html>", content)
|
|
self.assertIn("Package Index", content)
|
|
# Should have no <a> tags
|
|
self.assertNotIn("<a href", content)
|
|
|
|
def test_files_are_sorted(self):
|
|
"""Test that files appear in sorted order in the index."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
# Provide files in unsorted order
|
|
local_files = [
|
|
Path(tmp) / "zzz-3.0.whl",
|
|
Path(tmp) / "aaa-1.0.whl",
|
|
Path(tmp) / "mmm-2.0.whl",
|
|
]
|
|
|
|
generate_simple_index(output, local_files)
|
|
|
|
content = output.read_text()
|
|
# Find positions of each link
|
|
pos_aaa = content.find("aaa-1.0.whl")
|
|
pos_mmm = content.find("mmm-2.0.whl")
|
|
pos_zzz = content.find("zzz-3.0.whl")
|
|
# Verify sorted order
|
|
self.assertLess(pos_aaa, pos_mmm)
|
|
self.assertLess(pos_mmm, pos_zzz)
|
|
|
|
def test_valid_html_structure(self):
|
|
"""Test that generated HTML has correct structure."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
generate_simple_index(output, [Path(tmp) / "test.whl"])
|
|
|
|
content = output.read_text()
|
|
# Check for required HTML elements
|
|
self.assertIn("<!DOCTYPE html>", content)
|
|
self.assertIn("<html>", content)
|
|
self.assertIn("<head>", content)
|
|
self.assertIn('<meta charset="utf-8">', content)
|
|
self.assertIn("</head>", content)
|
|
self.assertIn("<body>", content)
|
|
self.assertIn("</body>", content)
|
|
self.assertIn("</html>", content)
|
|
# Check it ends with newline
|
|
self.assertTrue(content.endswith("\n"))
|
|
|
|
def test_plus_in_filename_is_url_encoded(self):
|
|
"""Test that '+' in filenames is percent-encoded in hrefs.
|
|
|
|
PEP 440 local versions produce filenames like
|
|
'pkg-1.0+localver-py3-none-any.whl'. S3 interprets a literal '+'
|
|
in URL paths as a space, so hrefs must encode '+' as '%2B'.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
output = Path(tmp) / "index.html"
|
|
local_files = [
|
|
Path(tmp) / "rocm_sdk_core-7.0.0+abc123-py3-none-linux_x86_64.whl",
|
|
]
|
|
parent_files = [
|
|
Path(tmp) / "rocm_sdk_libs-7.0.0+abc123-py3-none-linux_x86_64.whl",
|
|
]
|
|
|
|
generate_simple_index(output, local_files, parent_files=parent_files)
|
|
|
|
content = output.read_text()
|
|
# href should have %2B, not literal +
|
|
self.assertIn(
|
|
'href="./rocm_sdk_core-7.0.0%2Babc123-py3-none-linux_x86_64.whl"',
|
|
content,
|
|
)
|
|
self.assertIn(
|
|
'href="../rocm_sdk_libs-7.0.0%2Babc123-py3-none-linux_x86_64.whl"',
|
|
content,
|
|
)
|
|
# Display text should still have literal +
|
|
self.assertIn(
|
|
">rocm_sdk_core-7.0.0+abc123-py3-none-linux_x86_64.whl</a>",
|
|
content,
|
|
)
|
|
|
|
|
|
class TestGenerateMultiarchIndexes(unittest.TestCase):
|
|
"""Tests for generate_multiarch_indexes()."""
|
|
|
|
def test_generates_per_family_indexes(self):
|
|
"""Test that indexes are generated for each family subdirectory."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
# Create generic packages at top level
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
# Create family subdirectories with packages
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
(dist_dir / "gfx94X-dcgpu" / "rocm_libs_gfx94x-1.0.whl").write_bytes(
|
|
b"gfx94x"
|
|
)
|
|
(dist_dir / "gfx120X-all").mkdir()
|
|
(dist_dir / "gfx120X-all" / "rocm_libs_gfx120x-1.0.whl").write_bytes(
|
|
b"gfx120x"
|
|
)
|
|
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
# Verify indexes were created
|
|
self.assertTrue((dist_dir / "gfx94X-dcgpu" / "index.html").is_file())
|
|
self.assertTrue((dist_dir / "gfx120X-all" / "index.html").is_file())
|
|
|
|
def test_family_index_includes_local_and_parent(self):
|
|
"""Test that family indexes include both local and parent files."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
# Create generic packages
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
(dist_dir / "rocm_sdk_devel-1.0.whl").write_bytes(b"devel")
|
|
# Create family directory
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
(dist_dir / "gfx94X-dcgpu" / "rocm_libs_gfx94x-1.0.whl").write_bytes(
|
|
b"gfx94x"
|
|
)
|
|
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
content = (dist_dir / "gfx94X-dcgpu" / "index.html").read_text()
|
|
# Should have local file with ./ prefix
|
|
self.assertIn(
|
|
'<a href="./rocm_libs_gfx94x-1.0.whl">rocm_libs_gfx94x-1.0.whl</a>',
|
|
content,
|
|
)
|
|
# Should have parent files with ../ prefix
|
|
self.assertIn(
|
|
'<a href="../rocm_sdk_core-1.0.whl">rocm_sdk_core-1.0.whl</a>', content
|
|
)
|
|
self.assertIn(
|
|
'<a href="../rocm_sdk_devel-1.0.whl">rocm_sdk_devel-1.0.whl</a>',
|
|
content,
|
|
)
|
|
|
|
def test_custom_title_per_family(self):
|
|
"""Test that each family gets a custom title."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
(dist_dir / "gfx120X-all").mkdir()
|
|
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
content_94x = (dist_dir / "gfx94X-dcgpu" / "index.html").read_text()
|
|
content_120x = (dist_dir / "gfx120X-all" / "index.html").read_text()
|
|
|
|
self.assertIn("ROCm Python Packages - gfx94X-dcgpu", content_94x)
|
|
self.assertIn("ROCm Python Packages - gfx120X-all", content_120x)
|
|
|
|
def test_no_subdirectories_raises(self):
|
|
"""Test that missing family subdirectories raises an error."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
|
|
with self.assertRaises(FileNotFoundError):
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
def test_empty_family_directory(self):
|
|
"""Test family directory with no packages."""
|
|
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() # Empty directory
|
|
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
# Index should still be created, just with no local files
|
|
content = (dist_dir / "gfx94X-dcgpu" / "index.html").read_text()
|
|
# Should have parent file
|
|
self.assertIn(
|
|
'<a href="../rocm_sdk_core-1.0.whl">rocm_sdk_core-1.0.whl</a>', content
|
|
)
|
|
# Should not have any ./ links (no local files)
|
|
self.assertNotIn('<a href="./', content)
|
|
|
|
def test_custom_patterns(self):
|
|
"""Test using custom file patterns."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
# Create files with non-default extensions
|
|
(dist_dir / "README.md").write_bytes(b"readme")
|
|
(dist_dir / "LICENSE.txt").write_bytes(b"license")
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
(dist_dir / "gfx94X-dcgpu" / "package.whl").write_bytes(b"whl")
|
|
|
|
# Only look for .whl files
|
|
generate_multiarch_indexes(dist_dir, patterns=["*.whl"])
|
|
|
|
content = (dist_dir / "gfx94X-dcgpu" / "index.html").read_text()
|
|
# Should include .whl
|
|
self.assertIn("package.whl", content)
|
|
# Should not include .md or .txt
|
|
self.assertNotIn("README.md", content)
|
|
self.assertNotIn("LICENSE.txt", content)
|
|
|
|
def test_tar_gz_and_whl_both_included(self):
|
|
"""Test default patterns include both .whl and .tar.gz."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "generic.whl").write_bytes(b"whl")
|
|
(dist_dir / "generic.tar.gz").write_bytes(b"tar")
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
(dist_dir / "gfx94X-dcgpu" / "specific.whl").write_bytes(b"whl")
|
|
(dist_dir / "gfx94X-dcgpu" / "specific.tar.gz").write_bytes(b"tar")
|
|
|
|
generate_multiarch_indexes(dist_dir)
|
|
|
|
content = (dist_dir / "gfx94X-dcgpu" / "index.html").read_text()
|
|
# Local files
|
|
self.assertIn("specific.whl", content)
|
|
self.assertIn("specific.tar.gz", content)
|
|
# Parent files
|
|
self.assertIn("generic.whl", content)
|
|
self.assertIn("generic.tar.gz", content)
|
|
|
|
|
|
class TestCli(unittest.TestCase):
|
|
"""Tests for generate_local_index.py CLI modes."""
|
|
|
|
def test_default_generates_flat_index(self):
|
|
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_libraries-1.0.whl").write_bytes(b"libraries")
|
|
(dist_dir / "rocm_sdk_device_gfx942-1.0.whl").write_bytes(b"device")
|
|
|
|
main([os.fspath(dist_dir)])
|
|
|
|
content = (dist_dir / "index.html").read_text()
|
|
self.assertIn(
|
|
'<a href="./rocm_sdk_core-1.0.whl">rocm_sdk_core-1.0.whl</a>',
|
|
content,
|
|
)
|
|
self.assertIn(
|
|
'<a href="./rocm_sdk_libraries-1.0.whl">rocm_sdk_libraries-1.0.whl</a>',
|
|
content,
|
|
)
|
|
self.assertIn(
|
|
'<a href="./rocm_sdk_device_gfx942-1.0.whl">rocm_sdk_device_gfx942-1.0.whl</a>',
|
|
content,
|
|
)
|
|
|
|
def test_default_honors_output(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
output = dist_dir / "custom.html"
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
|
|
main([os.fspath(dist_dir), "--output", os.fspath(output)])
|
|
|
|
self.assertTrue(output.is_file())
|
|
self.assertFalse((dist_dir / "index.html").exists())
|
|
|
|
def test_default_honors_patterns(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
(dist_dir / "rocm-1.0.tar.gz").write_bytes(b"sdist")
|
|
|
|
main([os.fspath(dist_dir), "--patterns", "*.whl"])
|
|
|
|
content = (dist_dir / "index.html").read_text()
|
|
self.assertIn("rocm_sdk_core-1.0.whl", content)
|
|
self.assertNotIn("rocm-1.0.tar.gz", content)
|
|
|
|
def test_default_url_encodes_plus(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
filename = "rocm_sdk_core-7.0.0+abc123-py3-none-any.whl"
|
|
(dist_dir / filename).write_bytes(b"core")
|
|
|
|
main([os.fspath(dist_dir)])
|
|
|
|
content = (dist_dir / "index.html").read_text()
|
|
self.assertIn(
|
|
'href="./rocm_sdk_core-7.0.0%2Babc123-py3-none-any.whl"',
|
|
content,
|
|
)
|
|
self.assertIn(f">{filename}</a>", content)
|
|
|
|
def test_per_family_indexes_generates_subdirectory_indexes(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "rocm_sdk_core-1.0.whl").write_bytes(b"core")
|
|
family_dir = dist_dir / "gfx94X-dcgpu"
|
|
family_dir.mkdir()
|
|
(family_dir / "rocm_sdk_libraries_gfx94x_dcgpu-1.0.whl").write_bytes(
|
|
b"libs"
|
|
)
|
|
|
|
main([os.fspath(dist_dir), "--per-family-indexes"])
|
|
|
|
self.assertTrue((family_dir / "index.html").is_file())
|
|
self.assertFalse((dist_dir / "index.html").exists())
|
|
|
|
def test_per_family_indexes_rejects_output(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
|
|
with redirect_stderr(StringIO()), self.assertRaises(SystemExit):
|
|
main(
|
|
[
|
|
os.fspath(dist_dir),
|
|
"--per-family-indexes",
|
|
"--output",
|
|
os.fspath(dist_dir / "index.html"),
|
|
]
|
|
)
|
|
|
|
def test_per_family_indexes_rejects_parent_dir(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dist_dir = Path(tmp)
|
|
parent_dir = dist_dir / "parent"
|
|
(dist_dir / "gfx94X-dcgpu").mkdir()
|
|
|
|
with redirect_stderr(StringIO()), self.assertRaises(SystemExit):
|
|
main(
|
|
[
|
|
os.fspath(dist_dir),
|
|
"--per-family-indexes",
|
|
"--parent-dir",
|
|
os.fspath(parent_dir),
|
|
]
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|