Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,19 @@ 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:
# 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
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)})"
Expand Down
104 changes: 104 additions & 0 deletions iron/tests/infrastructure/conftest_lazy_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/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 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
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()]
root_conftest.pytest_collection_modifyitems(config=None, items=items)
assert all(item.markers_added == [] for item in items)


def test_device_probed_and_unsupported_items_skipped_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"