Skip to content

Commit 0ec3250

Browse files
authored
[None][refactor] Enhance pytest integration by updating test node generation to support fixture inheritance and dynamic collection (#15374)
Signed-off-by: yufeiwu-nv <230315618+yufeiwu-nv@users.noreply.github.com>
1 parent 09449d4 commit 0ec3250

1 file changed

Lines changed: 66 additions & 39 deletions

File tree

tests/integration/defs/perf/utils.py

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import abc
1616
import contextlib
1717
import copy
18+
import inspect
1819
import io
1920
import os
2021
import re
@@ -27,8 +28,7 @@
2728
from pathlib import Path
2829
from typing import Dict, List, NamedTuple, Optional
2930

30-
from _pytest.nodes import Item
31-
from _pytest.python import Function
31+
import pytest
3232
from defs.trt_test_alternative import print_error, print_info
3333

3434
from ..common import get_trt_llm_lib_dir
@@ -798,51 +798,71 @@ def _write_result(self, full_test_name: str,
798798
os.path.join(output_dir, yaml_name)))
799799

800800

801-
def generate_one_test_node(session, config, domain_name, test_name, test_func):
801+
def _find_collected_dir_node(items, rootpath):
802+
"""Return the real rootdir ``Dir`` collection node created by pytest.
803+
804+
Since pytest 9.1, fixture visibility is matched by the actual collection-node
805+
hierarchy instead of the textual nodeid (pytest issue #11785). Dynamically
806+
generated perf nodes must therefore descend from this real ``Dir`` node to
807+
inherit the fixtures declared in ``conftest.py``.
808+
"""
809+
for item in items:
810+
node = item
811+
while node is not None:
812+
if isinstance(node, pytest.Dir) and node.path == rootpath:
813+
return node
814+
node = node.parent
815+
return None
816+
817+
818+
def generate_one_test_node(session,
819+
config,
820+
domain_name,
821+
test_name,
822+
test_func,
823+
parent_dir=None,
824+
module_cache=None):
802825
"""
803826
A helper function to create a PyTest item with the specific name and specific test function.
804827
"""
805828

806-
# Create the parent Item node.
807-
# Pytest 8.x upgrade compatibility.
808-
# We should never import Pytest internals within test-definitions.
829+
# Attach the node under the real Dir -> Module hierarchy so it inherits the
830+
# fixtures declared in conftest.py. Since pytest 9.1 fixture visibility is
831+
# matched by the collection-node hierarchy rather than the textual nodeid
832+
# (pytest issue #11785), a node attached directly to the session can no longer
833+
# resolve those fixtures. The legacy nodeid is restored afterwards so that
834+
# test-list filtering and --test-prefix rewriting keep working unchanged.
835+
if parent_dir is not None:
836+
if module_cache is None:
837+
module_cache = {}
838+
module_path = Path(inspect.getfile(test_func)).resolve()
839+
module = module_cache.get(module_path)
840+
if module is None:
841+
module = pytest.Module.from_parent(parent_dir, path=module_path)
842+
module_cache[module_path] = module
843+
item = pytest.Function.from_parent(module,
844+
name=test_name,
845+
callobj=test_func)
846+
item.obj = test_func
847+
item._nodeid = f"{domain_name}::{test_name}"
848+
return item
849+
850+
# Fallback when no collected Dir node is available (e.g. nothing was collected
851+
# before the dynamic generation): mirror the legacy session-attached behavior.
809852
# TODO: TRT-23565
810-
parent = None
811-
if hasattr(Item, "from_parent"):
812-
813-
class TrtexecItem(Item):
814-
815-
def __init__(self, **kwargs):
816-
super().__init__(**kwargs)
853+
class TrtexecItem(pytest.Item):
817854

818-
def runtest(self):
819-
return test_func()
820-
821-
parent = TrtexecItem.from_parent(session,
822-
name=domain_name,
823-
nodeid=domain_name)
824-
else:
825-
parent = Item(name=domain_name,
826-
parent=session,
827-
config=config,
828-
session=session,
829-
nodeid=domain_name)
855+
def runtest(self):
856+
return test_func()
830857

858+
parent = TrtexecItem.from_parent(session,
859+
name=domain_name,
860+
nodeid=domain_name)
831861
parent.obj = None
832862

833-
# Create the Function node for the test.
834-
# For pytest 8.x compatibility
835-
# TODO: TRT-23565
836-
item = None
837-
if hasattr(Function, "from_parent"):
838-
item = Function.from_parent(parent, name=test_name, callobj=test_func)
839-
else:
840-
item = Function(name=test_name,
841-
parent=parent,
842-
config=config,
843-
session=session,
844-
callobj=test_func)
845-
863+
item = pytest.Function.from_parent(parent,
864+
name=test_name,
865+
callobj=test_func)
846866
item.obj = test_func
847867

848868
# This has to be set but can be random as it isn't used.
@@ -869,6 +889,12 @@ def generate_test_nodes(session, config, items, valid_prefixes: List[str],
869889
except FileNotFoundError:
870890
pass
871891

892+
# Reuse the real rootdir Dir node that pytest created during collection so the
893+
# generated nodes inherit conftest fixtures (see generate_one_test_node). The
894+
# module node is created once and shared across all generated cases.
895+
parent_dir = _find_collected_dir_node(items, config.rootpath)
896+
module_cache = {}
897+
872898
# Go through all test names and find the ones that need to be generated.
873899
for test_name in all_tests:
874900

@@ -882,7 +908,8 @@ def generate_test_nodes(session, config, items, valid_prefixes: List[str],
882908
# Generate the test node and append it to the Pytest item list.
883909
items.append(
884910
generate_one_test_node(session, config, domain_name,
885-
short_test_name, test_func))
911+
short_test_name, test_func, parent_dir,
912+
module_cache))
886913
print(f"Dynamically generated test node: {test_name}")
887914

888915
return items

0 commit comments

Comments
 (0)