From ca63e026429c0a40ff836e72dcf7c0fa9dd2385a Mon Sep 17 00:00:00 2001 From: tonde Date: Mon, 11 May 2026 15:34:12 +0200 Subject: [PATCH 1/5] tests: enable existing GPU tests for HIP builds and add ROCm-specific tests All C++ GPU test instantiations were guarded with `#ifdef CT2_WITH_CUDA`, which excluded them from HIP builds even though HIP uses the same Device::CUDA enum and code path. Change each guard to `#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP)` so the tests run on both backends. Add a `require_rocm` pytest marker to `test_utils.py` (currently backed by `get_cuda_device_count()` since HIP exposes devices via the same API) and a new `python/tests/test_rocm.py` with ROCm-specific device detection, compute type, and StorageView tests. --- python/tests/test_rocm.py | 58 ++++++++++++++++++++++++++++++++++++++ python/tests/test_utils.py | 5 ++++ tests/layers_test.cc | 2 +- tests/ops_test.cc | 2 +- tests/primitives_test.cc | 2 +- tests/storage_view_test.cc | 2 +- tests/translator_test.cc | 2 +- 7 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 python/tests/test_rocm.py diff --git a/python/tests/test_rocm.py b/python/tests/test_rocm.py new file mode 100644 index 000000000..879bcb55e --- /dev/null +++ b/python/tests/test_rocm.py @@ -0,0 +1,58 @@ +"""Tests specific to the ROCm/HIP GPU backend.""" + +import numpy as np +import pytest + +import ctranslate2 + +from test_utils import require_rocm + + +@require_rocm +class TestROCmDeviceDetection: + def test_device_count_positive(self): + assert ctranslate2.get_cuda_device_count() >= 1 + + def test_device_name_non_empty(self): + name = ctranslate2.get_device_name("cuda", 0) + assert isinstance(name, str) and len(name) > 0 + + +@require_rocm +class TestROCmComputeTypes: + def test_float32_supported(self): + types = ctranslate2.get_supported_compute_types("cuda") + assert "float32" in types + + def test_float16_supported(self): + types = ctranslate2.get_supported_compute_types("cuda") + assert "float16" in types + + def test_bfloat16_supported(self): + types = ctranslate2.get_supported_compute_types("cuda") + assert "bfloat16" in types + + def test_int8_supported(self): + types = ctranslate2.get_supported_compute_types("cuda") + assert "int8" in types + + +@require_rocm +class TestROCmStorageView: + def test_allocate_on_gpu(self): + x = ctranslate2.StorageView([4], ctranslate2.DataType.FLOAT32, device="cuda") + assert x.device == "cuda" + assert x.shape == [4] + + def test_cpu_to_gpu_roundtrip(self): + data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32) + x = ctranslate2.StorageView.from_array(data) + x_gpu = x.to("cuda") + x_back = x_gpu.to("cpu") + np.testing.assert_array_equal(np.array(x_back), data) + + def test_float16_on_gpu(self): + data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float16) + x = ctranslate2.StorageView.from_array(data, device="cuda") + assert x.dtype == ctranslate2.DataType.FLOAT16 + assert x.device == "cuda" diff --git a/python/tests/test_utils.py b/python/tests/test_utils.py index 3c5580b8d..28143f5cc 100644 --- a/python/tests/test_utils.py +++ b/python/tests/test_utils.py @@ -49,6 +49,11 @@ def array_equal(a, b): ctranslate2.get_cuda_device_count() == 0, reason="Test case requires a CUDA device" ) +# HIP builds expose ROCm devices through the same CUDA device count API. +require_rocm = pytest.mark.skipif( + ctranslate2.get_cuda_device_count() == 0, reason="Test case requires a ROCm/HIP device" +) + on_available_devices = pytest.mark.parametrize( "device", ["cpu"] + (["cuda"] if ctranslate2.get_cuda_device_count() > 0 else []) ) diff --git a/tests/layers_test.cc b/tests/layers_test.cc index cbbaa2d72..0ff925468 100644 --- a/tests/layers_test.cc +++ b/tests/layers_test.cc @@ -261,7 +261,7 @@ TEST(LayerTest, PositionEncoderNoSharedState) { INSTANTIATE_TEST_SUITE_P(CPU, LayerDeviceFPTest, ::testing::Values(FloatType{Device::CPU, DataType::FLOAT32, 1e-5}), fp_test_name); -#ifdef CT2_WITH_CUDA +#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP) INSTANTIATE_TEST_SUITE_P(CUDA, LayerDeviceFPTest, ::testing::Values(FloatType{Device::CUDA, DataType::FLOAT32, 1e-5}, FloatType{Device::CUDA, DataType::FLOAT16, 1e-2}, diff --git a/tests/ops_test.cc b/tests/ops_test.cc index cf0a7cc6b..a50d2b934 100644 --- a/tests/ops_test.cc +++ b/tests/ops_test.cc @@ -1414,7 +1414,7 @@ INSTANTIATE_TEST_SUITE_P(CPU, OpDeviceTest, ::testing::Values(Device::CPU)); INSTANTIATE_TEST_SUITE_P(CPU, OpDeviceFPTest, ::testing::Values(FloatType{Device::CPU, DataType::FLOAT32, 1e-5}), fp_test_name); -#ifdef CT2_WITH_CUDA +#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP) INSTANTIATE_TEST_SUITE_P(CUDA, OpDeviceTest, ::testing::Values(Device::CUDA)); INSTANTIATE_TEST_SUITE_P(CUDA, OpDeviceFPTest, ::testing::Values(FloatType{Device::CUDA, DataType::FLOAT32, 1e-5}, diff --git a/tests/primitives_test.cc b/tests/primitives_test.cc index 9f603de33..7fd0d3014 100644 --- a/tests/primitives_test.cc +++ b/tests/primitives_test.cc @@ -52,6 +52,6 @@ TEST_P(PrimitiveTest, PenalizePreviousTokens) { } INSTANTIATE_TEST_SUITE_P(CPU, PrimitiveTest, ::testing::Values(Device::CPU)); -#ifdef CT2_WITH_CUDA +#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP) INSTANTIATE_TEST_SUITE_P(CUDA, PrimitiveTest, ::testing::Values(Device::CUDA)); #endif diff --git a/tests/storage_view_test.cc b/tests/storage_view_test.cc index bc6da8825..0933c0105 100644 --- a/tests/storage_view_test.cc +++ b/tests/storage_view_test.cc @@ -75,6 +75,6 @@ TEST_P(StorageViewDeviceTest, HalfConversion) { } INSTANTIATE_TEST_SUITE_P(CPU, StorageViewDeviceTest, ::testing::Values(Device::CPU)); -#ifdef CT2_WITH_CUDA +#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP) INSTANTIATE_TEST_SUITE_P(CUDA, StorageViewDeviceTest, ::testing::Values(Device::CUDA)); #endif diff --git a/tests/translator_test.cc b/tests/translator_test.cc index c3edc3b5f..9a8006301 100644 --- a/tests/translator_test.cc +++ b/tests/translator_test.cc @@ -715,7 +715,7 @@ TEST_P(BiasedDecodingDeviceFPTest, NonZeroTimestepDiverge) { INSTANTIATE_TEST_SUITE_P(CPU, BiasedDecodingDeviceFPTest, ::testing::Values(FloatType{Device::CPU, DataType::FLOAT32}), fp_test_name); -#ifdef CT2_WITH_CUDA +#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP) INSTANTIATE_TEST_SUITE_P(CUDA, BiasedDecodingDeviceFPTest, ::testing::Values(FloatType{Device::CUDA, DataType::FLOAT32}, FloatType{Device::CUDA, DataType::FLOAT16}), From b5795053e8dcf9226f48d6e14944c8bc7465d0a2 Mon Sep 17 00:00:00 2001 From: tonde Date: Mon, 11 May 2026 15:41:30 +0200 Subject: [PATCH 2/5] tests: fix StorageView and DataType API usage in test_rocm.py Use lowercase enum values (DataType.float32, Device.cuda) and to_device() instead of to() for device transfers, matching the actual ctranslate2 Python API. --- python/tests/test_rocm.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/python/tests/test_rocm.py b/python/tests/test_rocm.py index 879bcb55e..39f456692 100644 --- a/python/tests/test_rocm.py +++ b/python/tests/test_rocm.py @@ -13,9 +13,8 @@ class TestROCmDeviceDetection: def test_device_count_positive(self): assert ctranslate2.get_cuda_device_count() >= 1 - def test_device_name_non_empty(self): - name = ctranslate2.get_device_name("cuda", 0) - assert isinstance(name, str) and len(name) > 0 + def test_device_enum_accessible(self): + assert ctranslate2.Device.cuda is not None @require_rocm @@ -40,19 +39,23 @@ def test_int8_supported(self): @require_rocm class TestROCmStorageView: def test_allocate_on_gpu(self): - x = ctranslate2.StorageView([4], ctranslate2.DataType.FLOAT32, device="cuda") - assert x.device == "cuda" - assert x.shape == [4] + data = np.zeros(4, dtype=np.float32) + x = ctranslate2.StorageView.from_array(data) + x_gpu = x.to_device(ctranslate2.Device.cuda) + assert x_gpu.device == "cuda" + assert x_gpu.shape == [4] def test_cpu_to_gpu_roundtrip(self): data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32) x = ctranslate2.StorageView.from_array(data) - x_gpu = x.to("cuda") - x_back = x_gpu.to("cpu") + x_gpu = x.to_device(ctranslate2.Device.cuda) + x_back = x_gpu.to_device(ctranslate2.Device.cpu) np.testing.assert_array_equal(np.array(x_back), data) def test_float16_on_gpu(self): - data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float16) - x = ctranslate2.StorageView.from_array(data, device="cuda") - assert x.dtype == ctranslate2.DataType.FLOAT16 - assert x.device == "cuda" + data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32) + x = ctranslate2.StorageView.from_array(data) + x_gpu = x.to_device(ctranslate2.Device.cuda) + x_fp16 = x_gpu.to(ctranslate2.DataType.float16) + assert x_fp16.dtype == ctranslate2.DataType.float16 + assert x_fp16.device == "cuda" From d9000071709fd8dcb3677ed21413ba0b283b3a9c Mon Sep 17 00:00:00 2001 From: tonde Date: Mon, 11 May 2026 15:51:38 +0200 Subject: [PATCH 3/5] style: fix black formatting in test_utils.py (trailing comma in skipif) --- python/tests/test_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/tests/test_utils.py b/python/tests/test_utils.py index 28143f5cc..c42ce4734 100644 --- a/python/tests/test_utils.py +++ b/python/tests/test_utils.py @@ -51,7 +51,8 @@ def array_equal(a, b): # HIP builds expose ROCm devices through the same CUDA device count API. require_rocm = pytest.mark.skipif( - ctranslate2.get_cuda_device_count() == 0, reason="Test case requires a ROCm/HIP device" + ctranslate2.get_cuda_device_count() == 0, + reason="Test case requires a ROCm/HIP device", ) on_available_devices = pytest.mark.parametrize( From f9408efde7601c2ba7088128e5dc2bec69f10d01 Mon Sep 17 00:00:00 2001 From: tonde Date: Mon, 11 May 2026 16:59:00 +0200 Subject: [PATCH 4/5] style: fix isort import order in test_rocm.py --- python/tests/test_rocm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tests/test_rocm.py b/python/tests/test_rocm.py index 39f456692..d264bcad1 100644 --- a/python/tests/test_rocm.py +++ b/python/tests/test_rocm.py @@ -3,10 +3,10 @@ import numpy as np import pytest -import ctranslate2 - from test_utils import require_rocm +import ctranslate2 + @require_rocm class TestROCmDeviceDetection: From bbc3d15f63adce5309d2ca16d9b555a14338af29 Mon Sep 17 00:00:00 2001 From: tonde Date: Tue, 12 May 2026 19:27:49 +0200 Subject: [PATCH 5/5] tests: drop test_rocm.py and require_rocm marker per #2042 review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @jordimas pointed out (https://github.com/OpenNMT/CTranslate2/pull/2042#issuecomment) that the Python side of this PR was redundant: `require_rocm` had the exact same body as `require_cuda` (both `get_cuda_device_count() == 0`, which already returns the count of ROCm/HIP devices on HIP builds), and `test_storageview_cuda` + `test_storageview_cuda_to_device` already cover the GPU StorageView allocation / round-trip / dtype paths that test_rocm.py would have exercised. Removing both files. The C++ side of this PR is unchanged — the `#if defined(CT2_WITH_CUDA) || defined(CT2_USE_HIP)` guard changes in ops_test.cc / primitives_test.cc / storage_view_test.cc / layers_test.cc / translator_test.cc are still required because those files explicitly test the CT2_WITH_CUDA preprocessor define rather than going through the device-count API. --- python/tests/test_rocm.py | 61 -------------------------------------- python/tests/test_utils.py | 6 ---- 2 files changed, 67 deletions(-) delete mode 100644 python/tests/test_rocm.py diff --git a/python/tests/test_rocm.py b/python/tests/test_rocm.py deleted file mode 100644 index d264bcad1..000000000 --- a/python/tests/test_rocm.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Tests specific to the ROCm/HIP GPU backend.""" - -import numpy as np -import pytest - -from test_utils import require_rocm - -import ctranslate2 - - -@require_rocm -class TestROCmDeviceDetection: - def test_device_count_positive(self): - assert ctranslate2.get_cuda_device_count() >= 1 - - def test_device_enum_accessible(self): - assert ctranslate2.Device.cuda is not None - - -@require_rocm -class TestROCmComputeTypes: - def test_float32_supported(self): - types = ctranslate2.get_supported_compute_types("cuda") - assert "float32" in types - - def test_float16_supported(self): - types = ctranslate2.get_supported_compute_types("cuda") - assert "float16" in types - - def test_bfloat16_supported(self): - types = ctranslate2.get_supported_compute_types("cuda") - assert "bfloat16" in types - - def test_int8_supported(self): - types = ctranslate2.get_supported_compute_types("cuda") - assert "int8" in types - - -@require_rocm -class TestROCmStorageView: - def test_allocate_on_gpu(self): - data = np.zeros(4, dtype=np.float32) - x = ctranslate2.StorageView.from_array(data) - x_gpu = x.to_device(ctranslate2.Device.cuda) - assert x_gpu.device == "cuda" - assert x_gpu.shape == [4] - - def test_cpu_to_gpu_roundtrip(self): - data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32) - x = ctranslate2.StorageView.from_array(data) - x_gpu = x.to_device(ctranslate2.Device.cuda) - x_back = x_gpu.to_device(ctranslate2.Device.cpu) - np.testing.assert_array_equal(np.array(x_back), data) - - def test_float16_on_gpu(self): - data = np.array([1.0, 2.0, 3.0, 4.0], dtype=np.float32) - x = ctranslate2.StorageView.from_array(data) - x_gpu = x.to_device(ctranslate2.Device.cuda) - x_fp16 = x_gpu.to(ctranslate2.DataType.float16) - assert x_fp16.dtype == ctranslate2.DataType.float16 - assert x_fp16.device == "cuda" diff --git a/python/tests/test_utils.py b/python/tests/test_utils.py index c42ce4734..3c5580b8d 100644 --- a/python/tests/test_utils.py +++ b/python/tests/test_utils.py @@ -49,12 +49,6 @@ def array_equal(a, b): ctranslate2.get_cuda_device_count() == 0, reason="Test case requires a CUDA device" ) -# HIP builds expose ROCm devices through the same CUDA device count API. -require_rocm = pytest.mark.skipif( - ctranslate2.get_cuda_device_count() == 0, - reason="Test case requires a ROCm/HIP device", -) - on_available_devices = pytest.mark.parametrize( "device", ["cpu"] + (["cuda"] if ctranslate2.get_cuda_device_count() > 0 else []) )