Files
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

256 lines
8.8 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
#
# SPDX-License-Identifier: MIT
"""
Shared fixtures for the hipFile Python binding tests.
The whole ``hipfile`` package is unimportable without its compiled Cython
extension ``hipfile._hipfile``: ``hipfile/__init__.py`` imports names from it at
import time, and it in turn needs the hipFile C library plus the HIP runtime. To
keep the suite hermetic -- no GPU, no AIS-capable storage, no build step -- we
register a pure-Python fake ``hipfile._hipfile`` in ``sys.modules`` *before* any
test module imports ``hipfile``. Because pytest imports this file during
collection, the injection below runs first.
Why a fake (real ints + lambdas) rather than ``Mock``/``MagicMock`` for this
module-level substrate:
* **A correctly-behaving mock is just a decorated fake.** The code needs real
ints from the enums (``IntEnum`` collapses members that share a value) and
correctly-shaped tuples from the callables (``err[0] != 0``,
``ver, err = hipFileGetVersion()``). Getting those out of mocks means
hand-configuring ``__int__`` on ~40 members and ``return_value`` on ~12
callables -- reconstructing this fake with more ceremony and nothing for
``autospec`` to check.
* **A fake carries no cross-test state.** This object lives in ``sys.modules``
for the whole session. A ``Mock`` would accumulate call history there and
retain any per-test ``return_value``/``side_effect``, so every test would have
to patch it just to get a clean baseline. The fake records nothing, so tests
that don't care about a call need no setup, and tests that do use
``unittest.mock.patch.object`` locally (auto-reverted at block exit).
"""
import sys
import types
from pathlib import Path
import pytest
# The pure-Python ``hipfile`` package lives one directory up (``python/``).
# Put it on sys.path so ``import hipfile`` resolves without an editable install
# (which would require building the Cython extension we are deliberately faking).
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
# --- Fake extension construction ------------------------------------------
# Fixed, arbitrary version so tests can assert a known value.
_FAKE_VERSION = (1, 2, 3)
# Every hipFileOpError_t name re-exported by hipfile/enums.py, in C order.
# enums.py builds an IntEnum from these, so each MUST get a UNIQUE int -- a
# collision would make the second name an IntEnum *alias* rather than a distinct
# member, silently changing len(OpError) and membership behavior. Success is 0
# because the high-level API tests ``err[0] != 0`` for failure.
_OP_ERROR_NAMES = (
"hipFileSuccess",
"hipFileDriverNotInitialized",
"hipFileDriverInvalidProps",
"hipFileDriverUnsupportedLimit",
"hipFileDriverVersionMismatch",
"hipFileDriverVersionReadError",
"hipFileDriverClosing",
"hipFilePlatformNotSupported",
"hipFileIONotSupported",
"hipFileDeviceNotSupported",
"hipFileDriverError",
"hipFileHipDriverError",
"hipFileHipPointerInvalid",
"hipFileHipMemoryTypeInvalid",
"hipFileHipPointerRangeError",
"hipFileHipContextMismatch",
"hipFileInvalidMappingSize",
"hipFileInvalidMappingRange",
"hipFileInvalidFileType",
"hipFileInvalidFileOpenFlag",
"hipFileDIONotSet",
"hipFileInvalidValue",
"hipFileMemoryAlreadyRegistered",
"hipFileMemoryNotRegistered",
"hipFilePermissionDenied",
"hipFileDriverAlreadyOpen",
"hipFileHandleNotRegistered",
"hipFileHandleAlreadyRegistered",
"hipFileDeviceNotFound",
"hipFileInternalError",
"hipFileGetNewFDFailed",
"hipFileDriverSetupError",
"hipFileIODisabled",
"hipFileBatchSubmitFailed",
"hipFileGPUMemoryPinningFailed",
"hipFileBatchFull",
"hipFileAsyncNotSupported",
"hipFileIOMaxError",
)
# hipFileFileHandleType_t names re-exported by enums.py -- also need unique ints.
_HANDLE_TYPE_NAMES = (
"hipFileHandleTypeOpaqueFD",
"hipFileHandleTypeOpaqueWin32",
"hipFileHandleTypeUserspaceFS",
)
# Sentinel returned by hipFileHandleRegister so tests can assert the FileHandle
# stored exactly what the extension handed back.
_FAKE_HANDLE = 0xF11E
_FAKE_PROPS = {
"nvfs_major_version": 1,
"nvfs_minor_version": 0,
"nvfs_poll_thresh_size": 0,
"nvfs_max_direct_io_size": 0,
"nvfs_driver_status_flags": 0,
"nvfs_driver_control_flags": 0,
"feature_flags": 0,
"max_device_cache_size": 0,
"per_buffer_cache_size": 0,
"max_device_pinned_mem_size": 0,
"max_batch_io_count": 0,
"max_batch_io_timeout_msecs": 0,
}
class _FakeAsyncIOHandle: # pylint: disable=too-few-public-methods
"""Stand-in for the real ``AsyncIOHandle`` cdef class.
The real class owns C storage the driver fills in when the async op
completes; here we just mirror its constructor and attribute surface
(``bytes_done`` read-only, ``size`` / ``file_offset`` / ``buffer_offset``
read-write) so ``file.py``'s ``read_async`` / ``write_async`` construct and
inspect it without a compiled extension.
"""
def __init__(self, size, file_offset, buffer_offset):
self.size = size
self.file_offset = file_offset
self.buffer_offset = buffer_offset
self.bytes_done = 0
def _build_fake_hipfile():
"""Create a fake ``hipfile._hipfile`` module.
Callable signatures mirror the real Cython wrappers so that
``patch.object(..., autospec=True)`` in tests enforces call arity. Defaults
are all success-shaped; error-path tests replace individual callables.
"""
mod = types.ModuleType("hipfile._hipfile")
# Version constants.
mod.VERSION_MAJOR, mod.VERSION_MINOR, mod.VERSION_PATCH = _FAKE_VERSION
# Enum values -- unique ints, Success == 0.
for value, name in enumerate(_OP_ERROR_NAMES):
setattr(mod, name, value)
for value, name in enumerate(_HANDLE_TYPE_NAMES):
setattr(mod, name, value)
# hipFileSuccess is set dynamically via setattr above, so pylint can't see it.
_success = (mod.hipFileSuccess, 0) # pylint: disable=no-member
# Driver lifecycle.
mod.hipFileDriverOpen = lambda: _success
mod.hipFileDriverClose = lambda: _success
mod.hipFileUseCount = lambda: 0
# Version / properties.
mod.hipFileGetVersion = lambda: (_FAKE_VERSION, _success)
mod.hipFileDriverGetProperties = lambda: (dict(_FAKE_PROPS), _success)
# File handles.
mod.hipFileHandleRegister = lambda handle_value, handle_type: (
_FAKE_HANDLE,
_success,
)
mod.hipFileHandleDeregister = lambda handle: None
# Buffer registration.
mod.hipFileBufRegister = lambda buffer_base, length, flags=0: _success
mod.hipFileBufDeregister = lambda buffer_base: _success
# Synchronous I/O -- default: full transfer, no error.
mod.hipFileRead = lambda handle, buffer_base, size, file_offset, buffer_offset: (
size,
0,
)
mod.hipFileWrite = lambda handle, buffer_base, size, file_offset, buffer_offset: (
size,
0,
)
# Asynchronous I/O -- default: full transfer, no error.
mod.AsyncIOHandle = _FakeAsyncIOHandle
mod.hipFileReadAsync = lambda handle, buffer_base, io, stream_handle: (0, 0)
mod.hipFileWriteAsync = lambda handle, buffer_base, io, stream_handle: (0, 0)
mod.hipFileStreamRegister = lambda stream_handle, flags=0: (0, 0)
mod.hipFileStreamDeregister = lambda stream_handle: (0, 0)
mod.supports_async = lambda: True
# Error strings.
mod.hipFileGetOpErrorString = lambda status: f"fake-op-error-{status}"
return mod
# Inject BEFORE any test imports hipfile. The ``not in sys.modules`` guard only
# avoids clobbering the module if something imported it first.
if "hipfile._hipfile" not in sys.modules:
sys.modules["hipfile._hipfile"] = _build_fake_hipfile()
# --- Fixtures --------------------------------------------------------------
@pytest.fixture
def fake_hipfile():
"""The injected fake ``hipfile._hipfile`` module."""
return sys.modules["hipfile._hipfile"]
@pytest.fixture
def fake_handle():
"""The opaque handle value returned by the fake ``hipFileHandleRegister``."""
return _FAKE_HANDLE
@pytest.fixture
def fake_version():
"""The ``(major, minor, patch)`` tuple the fake reports."""
return _FAKE_VERSION
class _FakeVoidP: # pylint: disable=too-few-public-methods
"""Minimal stand-in for ``ctypes.c_void_p`` (only ``.value`` is used)."""
def __init__(self, value):
self.value = value
@pytest.fixture
def fake_void_p():
"""Factory for ``ctypes.c_void_p``-like objects (Buffer.from_ctypes_void_p)."""
return _FakeVoidP
class _FakeBuffer: # pylint: disable=too-few-public-methods
"""Minimal Buffer stand-in exposing ``.ptr`` for FileHandle read/write."""
def __init__(self, ptr=0x1000):
self.ptr = ptr
@pytest.fixture
def fake_buffer():
"""A Buffer-like object with a ``.ptr`` attribute."""
return _FakeBuffer()