From ace8b1884b496a45d78b3cc724a62840ab1bb768 Mon Sep 17 00:00:00 2001 From: Taimuraz Kaitmazov Date: Tue, 8 Sep 2026 18:38:28 +0300 Subject: [PATCH 1/2] conftest: only resolve the NPU device when a test is device-restricted pytest_collection_modifyitems called aie_utils.DefaultNPURuntime.device() unconditionally for every collection, so a plain pytest in this tree opened the NPU regardless of what was selected -- contending with whatever else is using the (single-tenant) device and failing outright with none attached. Skip the probe when no collected item carries @pytest.mark.supported_devices. --- conftest.py | 16 ++- .../infrastructure/conftest_lazy_device.py | 106 ++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 iron/tests/infrastructure/conftest_lazy_device.py diff --git a/conftest.py b/conftest.py index ce6a4bb5f7..a461c76f82 100644 --- a/conftest.py +++ b/conftest.py @@ -175,10 +175,20 @@ def pytest_configure(config): def pytest_collection_modifyitems(config, items): + marked_items = [ + (item, item.get_closest_marker("supported_devices")) for item in items + ] + marked_items = [(item, marker) for item, marker in marked_items if marker] + if not marked_items: + # No collected test restricts itself to specific devices, so nothing + # here needs the NPU. Resolving it anyway made a plain `pytest` + # collection open the device unconditionally, contending with + # whatever else is using it and failing outright with no NPU at all. + return + device = aie_utils.DefaultNPURuntime.device().resolve().name - for item in items: - marker = item.get_closest_marker("supported_devices") - if marker and device not in marker.args: + for item, marker in marked_items: + if device not in marker.args: item.add_marker( pytest.mark.skip( reason=f"Not supported on {device} (supported: {', '.join(marker.args)})" diff --git a/iron/tests/infrastructure/conftest_lazy_device.py b/iron/tests/infrastructure/conftest_lazy_device.py new file mode 100644 index 0000000000..4b5fc6cf68 --- /dev/null +++ b/iron/tests/infrastructure/conftest_lazy_device.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""The root conftest.py's pytest_collection_modifyitems must not resolve a +device unless some collected test actually restricts itself to specific +devices via @pytest.mark.supported_devices. + +Before this fix it called aie_utils.DefaultNPURuntime.device() unconditionally +at collection time, so a plain `pytest` in this tree opened the NPU +regardless of which test was selected -- contending with anything else +using the (single-tenant) device, and failing outright with no NPU present. + +Loads the real root conftest.py by path (rather than depending on pytest's +own conftest-loading, which would defeat the point of testing it in +isolation) and calls its hook directly with fake items and a stubbed +aie_utils.DefaultNPURuntime that raises if .device() is ever called. +""" + +import importlib.util +import sys +from pathlib import Path +from types import SimpleNamespace + +import pytest + +_ROOT_CONFTEST = Path(__file__).resolve().parents[3] / "conftest.py" + + +def _load_root_conftest(): + spec = importlib.util.spec_from_file_location("_root_conftest_under_test", _ROOT_CONFTEST) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class _FakeMarker: + def __init__(self, *args): + self.args = args + + +class _FakeItem: + def __init__(self, marker=None): + self._marker = marker + self.markers_added = [] + + def get_closest_marker(self, name): + assert name == "supported_devices" + return self._marker + + def add_marker(self, marker): + self.markers_added.append(marker) + + +class _DeviceCalledError(Exception): + pass + + +def _stub_runtime_that_forbids_device_calls(root_conftest, monkeypatch): + def _raise(): + raise _DeviceCalledError( + "DefaultNPURuntime.device() was called with no marked test collected" + ) + + monkeypatch.setattr( + root_conftest.aie_utils, + "DefaultNPURuntime", + SimpleNamespace(device=_raise), + ) + + +def test_no_device_probe_when_nothing_is_device_restricted(monkeypatch): + root_conftest = _load_root_conftest() + _stub_runtime_that_forbids_device_calls(root_conftest, monkeypatch) + + items = [_FakeItem(), _FakeItem(), _FakeItem()] + # Must not raise _DeviceCalledError. + root_conftest.pytest_collection_modifyitems(config=None, items=items) + assert all(item.markers_added == [] for item in items) + + +def test_device_probed_and_marker_logic_preserved_when_a_test_is_restricted(monkeypatch): + root_conftest = _load_root_conftest() + + class _FakeDevice: + def resolve(self): + return SimpleNamespace(name="npu2") + + monkeypatch.setattr( + root_conftest.aie_utils, + "DefaultNPURuntime", + SimpleNamespace(device=lambda: _FakeDevice()), + ) + + unrestricted = _FakeItem() + matches_device = _FakeItem(_FakeMarker("npu1", "npu2")) + excludes_device = _FakeItem(_FakeMarker("npu1")) + + root_conftest.pytest_collection_modifyitems( + config=None, items=[unrestricted, matches_device, excludes_device] + ) + + assert unrestricted.markers_added == [] + assert matches_device.markers_added == [] + assert len(excludes_device.markers_added) == 1 + assert excludes_device.markers_added[0].name == "skip" From e0ccd016304b8f7a2a5b2cea82f82e45808e3778 Mon Sep 17 00:00:00 2001 From: Taimuraz Kaitmazov Date: Sat, 12 Sep 2026 01:26:15 +0300 Subject: [PATCH 2/2] conftest: state the invariant instead of narrating the fix The module docstring and the hook comment framed the change against the unconditional-resolve path, and the second test's name carried "preserved". Also black-formats a call the branch had left unformatted. --- conftest.py | 7 ++--- .../infrastructure/conftest_lazy_device.py | 28 +++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/conftest.py b/conftest.py index a461c76f82..3cd72a36e2 100644 --- a/conftest.py +++ b/conftest.py @@ -180,10 +180,9 @@ def pytest_collection_modifyitems(config, items): ] marked_items = [(item, marker) for item, marker in marked_items if marker] if not marked_items: - # No collected test restricts itself to specific devices, so nothing - # here needs the NPU. Resolving it anyway made a plain `pytest` - # collection open the device unconditionally, contending with - # whatever else is using it and failing outright with no NPU at all. + # Nothing collected needs the NPU. Resolving one here would open the + # single-tenant device at collection time, contending with whatever + # else holds it and erroring out when none is attached. return device = aie_utils.DefaultNPURuntime.device().resolve().name diff --git a/iron/tests/infrastructure/conftest_lazy_device.py b/iron/tests/infrastructure/conftest_lazy_device.py index 4b5fc6cf68..3ea80fcc42 100644 --- a/iron/tests/infrastructure/conftest_lazy_device.py +++ b/iron/tests/infrastructure/conftest_lazy_device.py @@ -3,18 +3,13 @@ # SPDX-License-Identifier: Apache-2.0 """The root conftest.py's pytest_collection_modifyitems must not resolve a -device unless some collected test actually restricts itself to specific -devices via @pytest.mark.supported_devices. - -Before this fix it called aie_utils.DefaultNPURuntime.device() unconditionally -at collection time, so a plain `pytest` in this tree opened the NPU -regardless of which test was selected -- contending with anything else -using the (single-tenant) device, and failing outright with no NPU present. - -Loads the real root conftest.py by path (rather than depending on pytest's -own conftest-loading, which would defeat the point of testing it in -isolation) and calls its hook directly with fake items and a stubbed -aie_utils.DefaultNPURuntime that raises if .device() is ever called. +device unless some collected test restricts itself to specific devices via +@pytest.mark.supported_devices. Resolving one unconditionally opens the +single-tenant NPU on every plain `pytest` in this tree, whatever was selected. + +pytest loads the root conftest.py for these tests too, so the hook under test +is imported by path instead and called directly, against fake items and a +stubbed aie_utils.DefaultNPURuntime that raises if .device() is reached. """ import importlib.util @@ -28,7 +23,9 @@ def _load_root_conftest(): - spec = importlib.util.spec_from_file_location("_root_conftest_under_test", _ROOT_CONFTEST) + spec = importlib.util.spec_from_file_location( + "_root_conftest_under_test", _ROOT_CONFTEST + ) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module @@ -74,12 +71,13 @@ def test_no_device_probe_when_nothing_is_device_restricted(monkeypatch): _stub_runtime_that_forbids_device_calls(root_conftest, monkeypatch) items = [_FakeItem(), _FakeItem(), _FakeItem()] - # Must not raise _DeviceCalledError. root_conftest.pytest_collection_modifyitems(config=None, items=items) assert all(item.markers_added == [] for item in items) -def test_device_probed_and_marker_logic_preserved_when_a_test_is_restricted(monkeypatch): +def test_device_probed_and_unsupported_items_skipped_when_a_test_is_restricted( + monkeypatch, +): root_conftest = _load_root_conftest() class _FakeDevice: