From f19f8d0fcee0d671d0ddeef0a660fe88fbd173a9 Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:03:47 -0400 Subject: [PATCH 1/6] fix(rocke): restore TF32 tests in installed artifacts Install the native recipe replay CLI in the test artifact and compare CBOR replay with Python LLVM lowering without attempting a source build. Run gfx942 numerics with Python when the native binding is absent and retain strict backend parity when it is available. --- .../hip-kernel-provider/rocke/TESTING.md | 16 ++++ .../rocke/platform/tests/CMakeLists.txt | 3 + .../rocke/platform/tests/conftest.py | 25 ++++++ .../rocke/platform/tests/core/test_tf32.py | 34 +++++--- .../tests/core/test_tf32_artifact_support.py | 81 +++++++++++++++++++ .../tests/numeric/test_gfx942_tf32_numeric.py | 16 ++-- 6 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py diff --git a/dnn-providers/hip-kernel-provider/rocke/TESTING.md b/dnn-providers/hip-kernel-provider/rocke/TESTING.md index fd80c8847dc3..e6535e5f875e 100644 --- a/dnn-providers/hip-kernel-provider/rocke/TESTING.md +++ b/dnn-providers/hip-kernel-provider/rocke/TESTING.md @@ -513,6 +513,22 @@ tests; an older local compiler failure is separate evidence. Review skipped tests explicitly: Torch-free execution, missing native fixtures, and unavailable GPUs each leave different coverage gaps. Host pytest is not a full GPU job replay. +TF32 recipe replay uses the prebuilt `rocke_portable_ir_replay_cli` installed +under `tests/portable_ir/`. It compares native CBOR replay with Python lowering +for both gfx942 atom shapes, every preparation mode, and LLVM 20/22/23. Missing +replay executables in an installed layout fail the test; installed tests never +attempt to configure a source build. In a source checkout, build that target and +set `ROCKE_REPLAY_CLI` to its executable path (a conventional `build/tests/` +location is also discovered). + +The gfx942 TF32 numerical test runs the Python engine's 12 variants against its +independent NumPy references when `rocke_engine` is absent. With the binding +available, it demands all 24 variants, native builder/lowerer byte identity, +and backend numerical parity. A discoverable but broken binding fails rather +than falling back. The standalone TF32 numerical CLI retains its strict +`--backend both` default. Set `ROCKE_REQUIRE_GFX942=1` to make a missing or +different GPU fail instead of skip. + ## 6. Invariants & contracts Cross-cutting properties every change must preserve: diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/CMakeLists.txt b/dnn-providers/hip-kernel-provider/rocke/platform/tests/CMakeLists.txt index 87d32004f5d2..94f501e42acf 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/CMakeLists.txt +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/CMakeLists.txt @@ -199,6 +199,9 @@ if(BUILD_TESTING) if(UNIX) target_link_libraries(rocke_portable_ir_replay_cli PRIVATE m) endif() + # Python recipe tests need the native VM in relocated test artifacts. + install(TARGETS rocke_portable_ir_replay_cli + RUNTIME DESTINATION "${ROCKE_TEST_INSTALL_DIR}/tests/portable_ir") endif() # --- instances layer -------------------------------------------------------- diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py index 40e4c42c28bd..cf442f7060f8 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py @@ -7,6 +7,7 @@ # # source tree: this dir -> rocke/platform/tests, so parent -> rocke/platform +import os import sys from pathlib import Path @@ -19,6 +20,30 @@ sys.path.insert(0, str(_PYROOT)) +@pytest.fixture(scope="session") +def native_recipe_replay_cli(): + """Use a prebuilt native VM; installed tests must never build from source.""" + suffix = ".exe" if sys.platform == "win32" else "" + name = f"rocke_portable_ir_replay_cli{suffix}" + explicit = os.environ.get("ROCKE_REPLAY_CLI") + if explicit: + path = Path(explicit).resolve() + if not path.is_file(): + pytest.fail(f"ROCKE_REPLAY_CLI does not name a file: {path}") + return path + installed = _HERE / "portable_ir" / name + if installed.is_file(): + return installed + if (_ROCKE / "rocke_installed_smoke.py").is_file(): + pytest.fail(f"Installed native recipe replay CLI is missing: {installed}") + built = _ROCKE / "build" / "tests" / name + if built.is_file(): + return built + pytest.skip( + "Build rocke_portable_ir_replay_cli and set ROCKE_REPLAY_CLI to its path" + ) + + @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): """Report a ``ROCKE_BACKEND=both`` coverage gap as a skip, never a pass. diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py index b06f1ede53a4..d94111e5e0a2 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py @@ -1,6 +1,8 @@ # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. # SPDX-License-Identifier: MIT """Logical TF32 contracts and strict native parity, independent of GPU access.""" +import subprocess + import numpy as np import pytest @@ -360,25 +362,33 @@ def test_parsed_mma_rejects_wrong_operands(): @pytest.mark.parametrize("m", [16, 32]) @pytest.mark.parametrize("mode", PREPARATIONS) -@pytest.mark.skip( - reason=( - "Temporarily disabled (#12612): native recipe replay attempts a source " - "build from installed test artifacts" - ) -) -def test_recipe_replay_preserves_ir(m, mode, monkeypatch): +def test_recipe_replay_preserves_ir(m, mode, tmp_path, native_recipe_replay_cli): from rocke.portable_ir.src.recording_builder import record_kernel - from rocke.portable_ir.src import online, recipe_bundle + from rocke.portable_ir.src import recipe_bundle spec = Tf32MmaProbeSpec(m, mode) recorded, recipe = record_kernel(lambda: build_tf32_mma_probe(spec)) assert serialize(recorded) == serialize(build_tf32_mma_probe(spec)) + path = tmp_path / "recipe.cbor" + path.write_bytes(recipe_bundle.cbor_encode(recipe)) for flavor in ("llvm20", "llvm22", "llvm23"): - monkeypatch.setenv("ROCKE_LLVM_FLAVOR", flavor) - replayed, _ = online.recipe_cbor_to_llvm( - recipe_bundle.cbor_encode(recipe), arch="gfx942" + replayed = subprocess.run( + [ + str(native_recipe_replay_cli), + "--recipe", + str(path), + "--cbor", + "--arch", + "gfx942", + "--flavor", + flavor, + ], + capture_output=True, + text=True, + check=True, + timeout=30, ) - assert replayed == _lower_kernel_to_llvm_python( + assert replayed.stdout == _lower_kernel_to_llvm_python( recorded, arch="gfx942", llvm_flavor=flavor ) diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py new file mode 100644 index 000000000000..aeb30a8388dc --- /dev/null +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py @@ -0,0 +1,81 @@ +# Copyright (c) Advanced Micro Devices, Inc., or its affiliates. +# SPDX-License-Identifier: MIT +"""Host checks for TF32 test wiring; no GPU numerical evidence.""" + +import runpy +import sys +from pathlib import Path + +import pytest + +_TESTS = Path(__file__).resolve().parents[1] + + +@pytest.mark.parametrize("layout", ["source", "installed"]) +def test_native_recipe_cli_discovery(tmp_path, monkeypatch, layout): + config = runpy.run_path(str(_TESTS / "conftest.py")) + fixture = config["native_recipe_replay_cli"].__wrapped__ + root = tmp_path / "platform" + tests = root / "tests" + fixture.__globals__.update(_HERE=tests, _ROCKE=root) + suffix = ".exe" if sys.platform == "win32" else "" + executable = f"rocke_portable_ir_replay_cli{suffix}" + monkeypatch.delenv("ROCKE_REPLAY_CLI", raising=False) + if layout == "installed": + root.mkdir(parents=True) + (root / "rocke_installed_smoke.py").touch() + cli = tests / "portable_ir" / executable + with pytest.raises(pytest.fail.Exception, match="CLI is missing"): + fixture() + else: + cli = root / "build" / "tests" / executable + with pytest.raises(pytest.skip.Exception, match="ROCKE_REPLAY_CLI"): + fixture() + cli.parent.mkdir(parents=True) + cli.touch() + assert fixture() == cli + explicit = tmp_path / executable + monkeypatch.setenv("ROCKE_REPLAY_CLI", str(explicit)) + with pytest.raises(pytest.fail.Exception, match="does not name a file"): + fixture() + explicit.touch() + assert fixture() == explicit + + +@pytest.mark.parametrize("native", [False, True]) +def test_gpu_test_selects_available_engines(tmp_path, monkeypatch, native): + module = runpy.run_path(str(_TESTS / "numeric" / "test_gfx942_tf32_numeric.py")) + test = module["test_gfx942_tf32_numeric"] + monkeypatch.setitem(test.__globals__, "get_device_arch", lambda: "gfx942") + monkeypatch.setattr( + module["importlib"].util, "find_spec", lambda _: object() if native else None + ) + expected = "both" if native else "python" + + def run(output_dir, backend): + assert output_dir == tmp_path + assert backend == expected + engines = ("python", "cpp") if native else ("python",) + return { + "status": "pass", + "results": [{"engine": engine} for engine in engines for _ in range(12)], + } + + monkeypatch.setitem(test.__globals__, "run", run) + test(tmp_path) + + def broken_run(*args, **kwargs): + raise ImportError("native binding is broken") + + monkeypatch.setitem(test.__globals__, "run", broken_run) + with pytest.raises(ImportError, match="native binding is broken"): + test(tmp_path) + + +def test_gpu_test_requires_requested_device(tmp_path, monkeypatch): + module = runpy.run_path(str(_TESTS / "numeric" / "test_gfx942_tf32_numeric.py")) + test = module["test_gfx942_tf32_numeric"] + monkeypatch.setitem(test.__globals__, "get_device_arch", lambda: "gfx950") + monkeypatch.setenv("ROCKE_REQUIRE_GFX942", "1") + with pytest.raises(pytest.fail.Exception, match="Required gfx942 device absent"): + test(tmp_path) diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/numeric/test_gfx942_tf32_numeric.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/numeric/test_gfx942_tf32_numeric.py index d05e493e3c04..f1539891dfda 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/numeric/test_gfx942_tf32_numeric.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/numeric/test_gfx942_tf32_numeric.py @@ -1,23 +1,23 @@ # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. # SPDX-License-Identifier: MIT +import importlib.util import os import pytest from rocke.runtime.hip_module import get_device_arch from rocke.examples.gfx942.tf32_numerics import run -@pytest.mark.skip( - reason=( - "Temporarily disabled (#12612): both-backend test requires rocke_engine, " - "absent from installed test artifacts" - ) -) def test_gfx942_tf32_numeric(tmp_path): arch = get_device_arch() if not arch or arch.split(":")[0] != "gfx942": if os.getenv("ROCKE_REQUIRE_GFX942") == "1": pytest.fail(f"Required gfx942 device absent: {arch}") pytest.skip("requires gfx942") - result = run(tmp_path, backend="both") + # The installed wheel has no pybind extension. Keep numerical coverage in + # that layout and demand native parity whenever the binding is supplied. + backend = "python" if importlib.util.find_spec("rocke_engine") is None else "both" + result = run(tmp_path, backend=backend) assert result["status"] == "pass" - assert len(result["results"]) == 24 + engines = {"python"} if backend == "python" else {"python", "cpp"} + assert {row["engine"] for row in result["results"]} == engines + assert len(result["results"]) == 12 * len(engines) From 5c9715d8901b3d943656880907de67899494afdf Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:44:24 -0400 Subject: [PATCH 2/6] test(rocke): isolate TF32 native replay fixture Keep the fixture local to the TF32 module and supply its executable through a dedicated CTest environment setting. Remove test-layout discovery without changing the replay settings or enrollment of other portable-IR tests. --- .../hip-kernel-provider/rocke/TESTING.md | 10 +++--- .../rocke/platform/CMakeLists.txt | 2 +- .../rocke/platform/tests/conftest.py | 25 --------------- .../rocke/platform/tests/core/test_tf32.py | 18 +++++++++-- .../tests/core/test_tf32_artifact_support.py | 32 ------------------- 5 files changed, 23 insertions(+), 64 deletions(-) diff --git a/dnn-providers/hip-kernel-provider/rocke/TESTING.md b/dnn-providers/hip-kernel-provider/rocke/TESTING.md index e6535e5f875e..6a697bfa43e2 100644 --- a/dnn-providers/hip-kernel-provider/rocke/TESTING.md +++ b/dnn-providers/hip-kernel-provider/rocke/TESTING.md @@ -516,10 +516,12 @@ GPUs each leave different coverage gaps. Host pytest is not a full GPU job repla TF32 recipe replay uses the prebuilt `rocke_portable_ir_replay_cli` installed under `tests/portable_ir/`. It compares native CBOR replay with Python lowering for both gfx942 atom shapes, every preparation mode, and LLVM 20/22/23. Missing -replay executables in an installed layout fail the test; installed tests never -attempt to configure a source build. In a source checkout, build that target and -set `ROCKE_REPLAY_CLI` to its executable path (a conventional `build/tests/` -location is also discovered). +replay executables configured by installed CTest fail the test; installed tests +never attempt to configure a source build. CTest supplies the executable through +`ROCKE_TEST_TF32_REPLAY_CLI`, relative to its working directory so the artifact +remains relocatable. In a source checkout, build that target and set the same +variable to its executable path. The fixture is local to the TF32 module and +does not use or modify `ROCKE_REPLAY_CLI`, which controls other portable-IR tests. The gfx942 TF32 numerical test runs the Python engine's 12 variants against its independent NumPy references when `rocke_engine` is absent. With the binding diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt b/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt index abeeafeb8ecb..68215a1d5ec3 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt +++ b/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt @@ -453,7 +453,7 @@ if(ROCKE_INSTALL_TESTS) string(APPEND _ROCKE_INSTALLED_CTEST_ENTRIES "set_tests_properties(rocke_pytest PROPERTIES\n" " LABELS \"ckc\\;python\\;quick\"\n" - " ENVIRONMENT \"PYTHONPATH=.\"\n" + " ENVIRONMENT \"PYTHONPATH=.;ROCKE_TEST_TF32_REPLAY_CLI=${ROCKE_TEST_CTEST_REL}tests/portable_ir/rocke_portable_ir_replay_cli${CMAKE_EXECUTABLE_SUFFIX}\"\n" " TIMEOUT 300)\n") endif() diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py index cf442f7060f8..40e4c42c28bd 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/conftest.py @@ -7,7 +7,6 @@ # # source tree: this dir -> rocke/platform/tests, so parent -> rocke/platform -import os import sys from pathlib import Path @@ -20,30 +19,6 @@ sys.path.insert(0, str(_PYROOT)) -@pytest.fixture(scope="session") -def native_recipe_replay_cli(): - """Use a prebuilt native VM; installed tests must never build from source.""" - suffix = ".exe" if sys.platform == "win32" else "" - name = f"rocke_portable_ir_replay_cli{suffix}" - explicit = os.environ.get("ROCKE_REPLAY_CLI") - if explicit: - path = Path(explicit).resolve() - if not path.is_file(): - pytest.fail(f"ROCKE_REPLAY_CLI does not name a file: {path}") - return path - installed = _HERE / "portable_ir" / name - if installed.is_file(): - return installed - if (_ROCKE / "rocke_installed_smoke.py").is_file(): - pytest.fail(f"Installed native recipe replay CLI is missing: {installed}") - built = _ROCKE / "build" / "tests" / name - if built.is_file(): - return built - pytest.skip( - "Build rocke_portable_ir_replay_cli and set ROCKE_REPLAY_CLI to its path" - ) - - @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): """Report a ``ROCKE_BACKEND=both`` coverage gap as a skip, never a pass. diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py index d94111e5e0a2..4a540873ab22 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py @@ -1,7 +1,9 @@ # Copyright (c) Advanced Micro Devices, Inc., or its affiliates. # SPDX-License-Identifier: MIT """Logical TF32 contracts and strict native parity, independent of GPU access.""" +import os import subprocess +from pathlib import Path import numpy as np import pytest @@ -360,9 +362,21 @@ def test_parsed_mma_rejects_wrong_operands(): native.lower_serialized_ir(ir, arch="gfx942") +@pytest.fixture(scope="module") +def tf32_recipe_replay_cli(): + """The launcher supplies TF32's native VM without changing other suites.""" + executable = os.environ.get("ROCKE_TEST_TF32_REPLAY_CLI") + if not executable: + pytest.skip("Set ROCKE_TEST_TF32_REPLAY_CLI to the prebuilt native replay CLI") + path = Path(executable).resolve() + if not path.is_file(): + pytest.fail(f"ROCKE_TEST_TF32_REPLAY_CLI does not name a file: {path}") + return path + + @pytest.mark.parametrize("m", [16, 32]) @pytest.mark.parametrize("mode", PREPARATIONS) -def test_recipe_replay_preserves_ir(m, mode, tmp_path, native_recipe_replay_cli): +def test_recipe_replay_preserves_ir(m, mode, tmp_path, tf32_recipe_replay_cli): from rocke.portable_ir.src.recording_builder import record_kernel from rocke.portable_ir.src import recipe_bundle @@ -374,7 +388,7 @@ def test_recipe_replay_preserves_ir(m, mode, tmp_path, native_recipe_replay_cli) for flavor in ("llvm20", "llvm22", "llvm23"): replayed = subprocess.run( [ - str(native_recipe_replay_cli), + str(tf32_recipe_replay_cli), "--recipe", str(path), "--cbor", diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py index aeb30a8388dc..d1fd7f6fe05c 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py @@ -3,7 +3,6 @@ """Host checks for TF32 test wiring; no GPU numerical evidence.""" import runpy -import sys from pathlib import Path import pytest @@ -11,37 +10,6 @@ _TESTS = Path(__file__).resolve().parents[1] -@pytest.mark.parametrize("layout", ["source", "installed"]) -def test_native_recipe_cli_discovery(tmp_path, monkeypatch, layout): - config = runpy.run_path(str(_TESTS / "conftest.py")) - fixture = config["native_recipe_replay_cli"].__wrapped__ - root = tmp_path / "platform" - tests = root / "tests" - fixture.__globals__.update(_HERE=tests, _ROCKE=root) - suffix = ".exe" if sys.platform == "win32" else "" - executable = f"rocke_portable_ir_replay_cli{suffix}" - monkeypatch.delenv("ROCKE_REPLAY_CLI", raising=False) - if layout == "installed": - root.mkdir(parents=True) - (root / "rocke_installed_smoke.py").touch() - cli = tests / "portable_ir" / executable - with pytest.raises(pytest.fail.Exception, match="CLI is missing"): - fixture() - else: - cli = root / "build" / "tests" / executable - with pytest.raises(pytest.skip.Exception, match="ROCKE_REPLAY_CLI"): - fixture() - cli.parent.mkdir(parents=True) - cli.touch() - assert fixture() == cli - explicit = tmp_path / executable - monkeypatch.setenv("ROCKE_REPLAY_CLI", str(explicit)) - with pytest.raises(pytest.fail.Exception, match="does not name a file"): - fixture() - explicit.touch() - assert fixture() == explicit - - @pytest.mark.parametrize("native", [False, True]) def test_gpu_test_selects_available_engines(tmp_path, monkeypatch, native): module = runpy.run_path(str(_TESTS / "numeric" / "test_gfx942_tf32_numeric.py")) From 20bd0a417adcc93ed4af14574831d07e9810a9ef Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:55:45 -0400 Subject: [PATCH 3/6] test(rocke): skip TF32 replay when native CLI is absent --- dnn-providers/hip-kernel-provider/rocke/TESTING.md | 7 ++++--- .../rocke/platform/tests/core/test_tf32.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dnn-providers/hip-kernel-provider/rocke/TESTING.md b/dnn-providers/hip-kernel-provider/rocke/TESTING.md index 6a697bfa43e2..4874f9cdb7ec 100644 --- a/dnn-providers/hip-kernel-provider/rocke/TESTING.md +++ b/dnn-providers/hip-kernel-provider/rocke/TESTING.md @@ -515,9 +515,10 @@ GPUs each leave different coverage gaps. Host pytest is not a full GPU job repla TF32 recipe replay uses the prebuilt `rocke_portable_ir_replay_cli` installed under `tests/portable_ir/`. It compares native CBOR replay with Python lowering -for both gfx942 atom shapes, every preparation mode, and LLVM 20/22/23. Missing -replay executables configured by installed CTest fail the test; installed tests -never attempt to configure a source build. CTest supplies the executable through +for both gfx942 atom shapes, every preparation mode, and LLVM 20/22/23. When the +replay executable is unset or missing, the native lane skips like the other +portable-IR replay tests. CLI execution errors and IR mismatches fail the test; +installed tests never attempt to configure a source build. CTest supplies it through `ROCKE_TEST_TF32_REPLAY_CLI`, relative to its working directory so the artifact remains relocatable. In a source checkout, build that target and set the same variable to its executable path. The fixture is local to the TF32 module and diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py index 4a540873ab22..46811ca4e3d8 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py @@ -370,7 +370,7 @@ def tf32_recipe_replay_cli(): pytest.skip("Set ROCKE_TEST_TF32_REPLAY_CLI to the prebuilt native replay CLI") path = Path(executable).resolve() if not path.is_file(): - pytest.fail(f"ROCKE_TEST_TF32_REPLAY_CLI does not name a file: {path}") + pytest.skip(f"TF32 native replay CLI not found: {path}") return path From 9936367778083efb7230cc14efb5cb6451bdee2f Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:04:25 -0400 Subject: [PATCH 4/6] docs(rocke): guide native replay test implementation --- .../hip-kernel-provider/rocke/TESTING.md | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/dnn-providers/hip-kernel-provider/rocke/TESTING.md b/dnn-providers/hip-kernel-provider/rocke/TESTING.md index 4874f9cdb7ec..9ffd549a16f7 100644 --- a/dnn-providers/hip-kernel-provider/rocke/TESTING.md +++ b/dnn-providers/hip-kernel-provider/rocke/TESTING.md @@ -513,24 +513,48 @@ tests; an older local compiler failure is separate evidence. Review skipped tests explicitly: Torch-free execution, missing native fixtures, and unavailable GPUs each leave different coverage gaps. Host pytest is not a full GPU job replay. -TF32 recipe replay uses the prebuilt `rocke_portable_ir_replay_cli` installed -under `tests/portable_ir/`. It compares native CBOR replay with Python lowering -for both gfx942 atom shapes, every preparation mode, and LLVM 20/22/23. When the -replay executable is unset or missing, the native lane skips like the other -portable-IR replay tests. CLI execution errors and IR mismatches fail the test; -installed tests never attempt to configure a source build. CTest supplies it through -`ROCKE_TEST_TF32_REPLAY_CLI`, relative to its working directory so the artifact -remains relocatable. In a source checkout, build that target and set the same -variable to its executable path. The fixture is local to the TF32 module and -does not use or modify `ROCKE_REPLAY_CLI`, which controls other portable-IR tests. - -The gfx942 TF32 numerical test runs the Python engine's 12 variants against its -independent NumPy references when `rocke_engine` is absent. With the binding -available, it demands all 24 variants, native builder/lowerer byte identity, -and backend numerical parity. A discoverable but broken binding fails rather -than falling back. The standalone TF32 numerical CLI retains its strict -`--backend both` default. Set `ROCKE_REQUIRE_GFX942=1` to make a missing or -different GPU fail instead of skip. +#### 5.1.3 Writing native recipe replay tests + +Native replay APIs can hide a source-build dependency. In particular, +[`online.recipe_cbor_to_llvm()`](platform/python/rocke/portable_ir/src/online.py) +calls `online.load()`, which calls `build_lib()` when it finds no prebuilt shared +library. A test can pass in a source checkout or with a cached library, then fail +in an installed artifact because the source tree and `CMakeLists.txt` are absent. +Do not let replay test setup implicitly configure or build native code. + +When adding or changing a native replay test: + +- Build the native artifact during build/setup, from the same revision as the + Python oracle. Run a prebuilt replay CLI, or supply a prebuilt shared-library + path explicitly before calling the online API. Never fall back to a cached + library or an automatic source build to make the test pass. +- If the prebuilt CLI is unset or missing, skip the native replay lane with a + reason identifying the missing artifact, following the existing portable-IR + tests. Once present, execution errors and IR mismatches are failures; do not + catch them as capability skips. Keep independent Python coverage enabled. +- Let the launcher supply artifact paths. Installed CTest paths must be relative + to its working directory so relocation works. Do not infer artifact presence + or location from test-file paths, nearby source files, or conventional build + directories. Install required content through CMake and the artifact manifest. +- Keep a suite-specific fixture local to its module. Do not change shared + environment settings or add an autouse fixture that changes other suites' + native-lane discovery or skips. A shared fixture needs an intentional consumer + contract and validation of every affected suite. + +The existing replay CLI target is `rocke_portable_ir_replay_cli`; its installed +location is `tests/portable_ir/`. Current launcher settings have separate consumers: + +| Setting | Consumer | +|---|---| +| `ROCKE_REPLAY_CLI` | Existing portable-IR replay suites | +| `ROCKE_TEST_TF32_REPLAY_CLI` | Module-local TF32 replay fixture, supplied by installed CTest | + +For source execution, build the target first and supply its executable through +the setting for the intended suite. Do not set another suite's variable as a +side effect. For validation, check absent and present artifacts, a broken +executable, and unrelated suites' skips. Repeat the native lane in a clean +relocated install without source directories or native caches available; a +source-only pass cannot establish installed-artifact support. ## 6. Invariants & contracts From 10477dcb13f51199a9fd534e14e3359cb716fcb2 Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:11:56 -0400 Subject: [PATCH 5/6] test(rocke): remove redundant TF32 wrapper checks --- .../tests/core/test_tf32_artifact_support.py | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py deleted file mode 100644 index d1fd7f6fe05c..000000000000 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32_artifact_support.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) Advanced Micro Devices, Inc., or its affiliates. -# SPDX-License-Identifier: MIT -"""Host checks for TF32 test wiring; no GPU numerical evidence.""" - -import runpy -from pathlib import Path - -import pytest - -_TESTS = Path(__file__).resolve().parents[1] - - -@pytest.mark.parametrize("native", [False, True]) -def test_gpu_test_selects_available_engines(tmp_path, monkeypatch, native): - module = runpy.run_path(str(_TESTS / "numeric" / "test_gfx942_tf32_numeric.py")) - test = module["test_gfx942_tf32_numeric"] - monkeypatch.setitem(test.__globals__, "get_device_arch", lambda: "gfx942") - monkeypatch.setattr( - module["importlib"].util, "find_spec", lambda _: object() if native else None - ) - expected = "both" if native else "python" - - def run(output_dir, backend): - assert output_dir == tmp_path - assert backend == expected - engines = ("python", "cpp") if native else ("python",) - return { - "status": "pass", - "results": [{"engine": engine} for engine in engines for _ in range(12)], - } - - monkeypatch.setitem(test.__globals__, "run", run) - test(tmp_path) - - def broken_run(*args, **kwargs): - raise ImportError("native binding is broken") - - monkeypatch.setitem(test.__globals__, "run", broken_run) - with pytest.raises(ImportError, match="native binding is broken"): - test(tmp_path) - - -def test_gpu_test_requires_requested_device(tmp_path, monkeypatch): - module = runpy.run_path(str(_TESTS / "numeric" / "test_gfx942_tf32_numeric.py")) - test = module["test_gfx942_tf32_numeric"] - monkeypatch.setitem(test.__globals__, "get_device_arch", lambda: "gfx950") - monkeypatch.setenv("ROCKE_REQUIRE_GFX942", "1") - with pytest.raises(pytest.fail.Exception, match="Required gfx942 device absent"): - test(tmp_path) From 7031c7c84a5674c8ad43c87ed689717a169d1232 Mon Sep 17 00:00:00 2001 From: Max Podkorytov <4273004+tenpercent@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:57:32 -0400 Subject: [PATCH 6/6] test(rocke): use one native replay CLI setting --- .../hip-kernel-provider/rocke/TESTING.md | 27 ++++++++++--------- .../rocke/platform/CMakeLists.txt | 2 +- .../rocke/platform/tests/core/test_tf32.py | 6 ++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/dnn-providers/hip-kernel-provider/rocke/TESTING.md b/dnn-providers/hip-kernel-provider/rocke/TESTING.md index 9ffd549a16f7..fc601a85fc7f 100644 --- a/dnn-providers/hip-kernel-provider/rocke/TESTING.md +++ b/dnn-providers/hip-kernel-provider/rocke/TESTING.md @@ -536,23 +536,24 @@ When adding or changing a native replay test: to its working directory so relocation works. Do not infer artifact presence or location from test-file paths, nearby source files, or conventional build directories. Install required content through CMake and the artifact manifest. -- Keep a suite-specific fixture local to its module. Do not change shared - environment settings or add an autouse fixture that changes other suites' - native-lane discovery or skips. A shared fixture needs an intentional consumer - contract and validation of every affected suite. +- Keep a suite-specific fixture local to its module. Do not mutate shared + environment settings during fixture setup or add an autouse fixture that + changes other suites' native-lane discovery or skips. Shared launcher wiring + or fixtures need an intentional consumer contract and validation of every + affected suite. The existing replay CLI target is `rocke_portable_ir_replay_cli`; its installed -location is `tests/portable_ir/`. Current launcher settings have separate consumers: - -| Setting | Consumer | -|---|---| -| `ROCKE_REPLAY_CLI` | Existing portable-IR replay suites | -| `ROCKE_TEST_TF32_REPLAY_CLI` | Module-local TF32 replay fixture, supplied by installed CTest | +location is `tests/portable_ir/`. `ROCKE_REPLAY_CLI` is the single launcher setting +for native CLI replay, including TF32 and the portable-IR suites. Installed CTest +supplies the relative executable path once for the pytest run, enabling all +consumers of that artifact. For source execution, build the target first and supply its executable through -the setting for the intended suite. Do not set another suite's variable as a -side effect. For validation, check absent and present artifacts, a broken -executable, and unrelated suites' skips. Repeat the native lane in a clean +`ROCKE_REPLAY_CLI`. Reuse this setting for new replay consumers rather than adding +a kernel-specific setting. When changing its launcher wiring, validate every +consumer, including suites whose native lanes previously skipped. Check absent +and present artifacts, a broken executable, and unrelated suites' skips. +Repeat the native lane in a clean relocated install without source directories or native caches available; a source-only pass cannot establish installed-artifact support. diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt b/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt index 68215a1d5ec3..67199dd4b853 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt +++ b/dnn-providers/hip-kernel-provider/rocke/platform/CMakeLists.txt @@ -453,7 +453,7 @@ if(ROCKE_INSTALL_TESTS) string(APPEND _ROCKE_INSTALLED_CTEST_ENTRIES "set_tests_properties(rocke_pytest PROPERTIES\n" " LABELS \"ckc\\;python\\;quick\"\n" - " ENVIRONMENT \"PYTHONPATH=.;ROCKE_TEST_TF32_REPLAY_CLI=${ROCKE_TEST_CTEST_REL}tests/portable_ir/rocke_portable_ir_replay_cli${CMAKE_EXECUTABLE_SUFFIX}\"\n" + " ENVIRONMENT \"PYTHONPATH=.;ROCKE_REPLAY_CLI=${ROCKE_TEST_CTEST_REL}tests/portable_ir/rocke_portable_ir_replay_cli${CMAKE_EXECUTABLE_SUFFIX}\"\n" " TIMEOUT 300)\n") endif() diff --git a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py index 46811ca4e3d8..3773bb5204eb 100644 --- a/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py +++ b/dnn-providers/hip-kernel-provider/rocke/platform/tests/core/test_tf32.py @@ -364,10 +364,10 @@ def test_parsed_mma_rejects_wrong_operands(): @pytest.fixture(scope="module") def tf32_recipe_replay_cli(): - """The launcher supplies TF32's native VM without changing other suites.""" - executable = os.environ.get("ROCKE_TEST_TF32_REPLAY_CLI") + """Use the launcher-supplied native VM shared by replay suites.""" + executable = os.environ.get("ROCKE_REPLAY_CLI") if not executable: - pytest.skip("Set ROCKE_TEST_TF32_REPLAY_CLI to the prebuilt native replay CLI") + pytest.skip("Set ROCKE_REPLAY_CLI to the prebuilt native replay CLI") path = Path(executable).resolve() if not path.is_file(): pytest.skip(f"TF32 native replay CLI not found: {path}")