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
198 lines
6.4 KiB
Python
198 lines
6.4 KiB
Python
# Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Tests for hipfile.file.FileHandle."""
|
|
|
|
# pylint: disable=missing-function-docstring,redefined-outer-name,protected-access
|
|
# pylint: disable=import-error # hipfile not installed in lint env; extension faked
|
|
|
|
import errno
|
|
import os
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
import hipfile.file
|
|
from hipfile.enums import FileHandleType, OpError
|
|
from hipfile.error import HipFileException
|
|
from hipfile.file import FileHandle
|
|
|
|
_SUCCESS = (OpError.SUCCESS, 0)
|
|
_FAILURE = (OpError.HANDLE_ALREADY_REGISTERED, 0)
|
|
_FAKE_FD = 11
|
|
_FAKE_HANDLE = 0xF11E
|
|
|
|
|
|
# --- handle_type setter guards --------------------------------------------
|
|
|
|
|
|
def test_handle_type_rejects_non_member():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with pytest.raises(ValueError):
|
|
fh.handle_type = 9999
|
|
|
|
|
|
def test_handle_type_rejects_win32():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with pytest.raises(NotImplementedError):
|
|
fh.handle_type = FileHandleType.OPAQUE_WIN32
|
|
|
|
|
|
def test_handle_type_rejects_change_while_open():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
fh._handle = _FAKE_HANDLE # simulate an open handle
|
|
with pytest.raises(RuntimeError):
|
|
fh.handle_type = FileHandleType.USERSPACE_FS
|
|
|
|
|
|
# --- open -----------------------------------------------------------------
|
|
|
|
|
|
def test_open_registers_and_stores_handle():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with mock.patch.object(
|
|
hipfile.file.os, "open", return_value=_FAKE_FD
|
|
), mock.patch.object(
|
|
hipfile.file,
|
|
"hipFileHandleRegister",
|
|
autospec=True,
|
|
return_value=(_FAKE_HANDLE, _SUCCESS),
|
|
) as register:
|
|
fh.open()
|
|
assert fh.handle == _FAKE_HANDLE
|
|
register.assert_called_once_with(_FAKE_FD, FileHandleType.OPAQUE_FD)
|
|
|
|
|
|
def test_open_closes_fd_when_registration_fails():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with mock.patch.object(
|
|
hipfile.file.os, "open", return_value=_FAKE_FD
|
|
), mock.patch.object(hipfile.file.os, "close") as os_close, mock.patch.object(
|
|
hipfile.file,
|
|
"hipFileHandleRegister",
|
|
autospec=True,
|
|
return_value=(0, _FAILURE),
|
|
):
|
|
with pytest.raises(HipFileException):
|
|
fh.open()
|
|
os_close.assert_called_once_with(_FAKE_FD)
|
|
assert fh.handle is None
|
|
|
|
|
|
def test_open_raises_when_already_open():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
fh._handle = _FAKE_HANDLE
|
|
with pytest.raises(RuntimeError):
|
|
fh.open()
|
|
|
|
|
|
# --- close ----------------------------------------------------------------
|
|
|
|
|
|
def test_close_deregisters_and_closes():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
fh._handle = _FAKE_HANDLE
|
|
fh._fd = _FAKE_FD
|
|
with mock.patch.object(
|
|
hipfile.file, "hipFileHandleDeregister", autospec=True
|
|
) as dereg, mock.patch.object(hipfile.file.os, "close") as os_close:
|
|
fh.close()
|
|
dereg.assert_called_once_with(_FAKE_HANDLE)
|
|
os_close.assert_called_once_with(_FAKE_FD)
|
|
assert fh.handle is None
|
|
assert fh._fd is None
|
|
|
|
|
|
def test_close_on_never_opened_is_noop():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with mock.patch.object(
|
|
hipfile.file, "hipFileHandleDeregister", autospec=True
|
|
) as dereg, mock.patch.object(hipfile.file.os, "close") as os_close:
|
|
fh.close()
|
|
dereg.assert_not_called()
|
|
os_close.assert_not_called()
|
|
|
|
|
|
def test_close_is_idempotent_after_open():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
fh._handle = _FAKE_HANDLE
|
|
fh._fd = _FAKE_FD
|
|
with mock.patch.object(
|
|
hipfile.file, "hipFileHandleDeregister", autospec=True
|
|
) as dereg, mock.patch.object(hipfile.file.os, "close") as os_close:
|
|
fh.close()
|
|
fh.close()
|
|
# Cleanup runs exactly once despite the repeated call.
|
|
dereg.assert_called_once_with(_FAKE_HANDLE)
|
|
os_close.assert_called_once_with(_FAKE_FD)
|
|
|
|
|
|
# --- read / write ---------------------------------------------------------
|
|
|
|
|
|
def _open_handle():
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
fh._handle = _FAKE_HANDLE
|
|
return fh
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["read", "write"])
|
|
def test_io_raises_when_not_open(method, fake_buffer):
|
|
fh = FileHandle("/tmp/x", os.O_RDONLY)
|
|
with pytest.raises(RuntimeError):
|
|
getattr(fh, method)(fake_buffer, 64, 0, 0)
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["read", "write"])
|
|
def test_io_success_returns_bytecount(method, fake_buffer):
|
|
fh = _open_handle()
|
|
wrapper = "hipFileRead" if method == "read" else "hipFileWrite"
|
|
with mock.patch.object(
|
|
hipfile.file, wrapper, autospec=True, return_value=(64, 0)
|
|
) as io_call:
|
|
result = getattr(fh, method)(fake_buffer, 64, 8, 16)
|
|
assert result == 64
|
|
io_call.assert_called_once_with(_FAKE_HANDLE, fake_buffer.ptr, 64, 8, 16)
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["read", "write"])
|
|
def test_io_system_error_raises_oserror(method, fake_buffer):
|
|
fh = _open_handle()
|
|
wrapper = "hipFileRead" if method == "read" else "hipFileWrite"
|
|
with mock.patch.object(
|
|
hipfile.file, wrapper, autospec=True, return_value=(-1, errno.EIO)
|
|
):
|
|
with pytest.raises(OSError) as excinfo:
|
|
getattr(fh, method)(fake_buffer, 64, 0, 0)
|
|
assert excinfo.value.errno == errno.EIO
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["read", "write"])
|
|
def test_io_hipfile_error_raises_exception(method, fake_buffer):
|
|
fh = _open_handle()
|
|
wrapper = "hipFileRead" if method == "read" else "hipFileWrite"
|
|
# result < -1 encodes a negated hipFileOpError_t with extra_err == 0.
|
|
op_err = OpError.INVALID_VALUE
|
|
with mock.patch.object(
|
|
hipfile.file, wrapper, autospec=True, return_value=(-int(op_err), 0)
|
|
):
|
|
with pytest.raises(HipFileException) as excinfo:
|
|
getattr(fh, method)(fake_buffer, 64, 0, 0)
|
|
assert excinfo.value.hipfile_err == op_err
|
|
assert excinfo.value.hip_err == 0
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["read", "write"])
|
|
def test_io_hip_driver_error_carries_hip_err(method, fake_buffer):
|
|
fh = _open_handle()
|
|
wrapper = "hipFileRead" if method == "read" else "hipFileWrite"
|
|
op_err = OpError.HIP_DRIVER_ERROR
|
|
with mock.patch.object(
|
|
hipfile.file, wrapper, autospec=True, return_value=(-int(op_err), 99)
|
|
):
|
|
with pytest.raises(HipFileException) as excinfo:
|
|
getattr(fh, method)(fake_buffer, 64, 0, 0)
|
|
assert excinfo.value.hipfile_err == op_err
|
|
assert excinfo.value.hip_err == 99
|