Skip to content

Commit f7722e2

Browse files
authored
[TRTLLM-4866] [test] Support waiving unit tests by waives.txt (NVIDIA#8359)
Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
1 parent 128a351 commit f7722e2

File tree

3 files changed

+102
-15
lines changed

3 files changed

+102
-15
lines changed

tests/integration/defs/test_list_parser.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def applyMarkerIfPrefix(marker, prefix, reason):
8383
return lines
8484

8585

86-
def parse_test_list_lines(test_list, lines, test_prefix):
86+
def parse_test_list_lines(test_list, lines, test_prefix, convert_unittest=True):
8787
"""Parses the lines of a test list. Test names returned contain all values within square brackets. Does not process
8888
each test id value.
8989
@@ -146,19 +146,21 @@ def parse_test_line(enumerated_line):
146146
test_list, lineno, reason_raw))
147147
break
148148

149-
# extract full:XXX/ prefix
150-
full_prefix = ""
151-
match = re.match(r'(full:.*?/)(.+)', test_name)
152-
if match:
153-
full_prefix = match.group(1)
154-
test_name = match.group(2)
149+
if convert_unittest:
150+
# extract full:XXX/ prefix
151+
full_prefix = ""
152+
match = re.match(r'(full:.*?/)(.+)', test_name)
153+
if match:
154+
full_prefix = match.group(1)
155+
test_name = match.group(2)
155156

156-
# convert unittest to actual test name
157-
if test_name.startswith("unittest/"):
158-
test_name = f"test_unittests.py::test_unittests_v2[{test_name}]"
157+
# convert unittest to actual test name
158+
if test_name.startswith("unittest/"):
159+
test_name = f"test_unittests.py::test_unittests_v2[{test_name}]"
160+
161+
# combine back
162+
test_name = full_prefix + test_name
159163

160-
# combine back
161-
test_name = full_prefix + test_name
162164
test_name = parse_test_name(test_name)
163165

164166
return (test_name, marker, reason, timeout)
@@ -585,8 +587,6 @@ def parse_and_validate_test_list(
585587
check_for_corrections,
586588
):
587589
test_prefix = config.getoption("--test-prefix")
588-
apply_test_list_correction = config.getoption(
589-
"--apply-test-list-correction")
590590
test_names, test_name_to_marker_dict = parse_test_list(
591591
test_list, test_prefix)
592592

@@ -597,6 +597,8 @@ def parse_and_validate_test_list(
597597
TestCorrectionMode.EXACT_MATCH,
598598
)
599599

600+
apply_test_list_correction = config.getoption(
601+
"--apply-test-list-correction")
600602
if apply_test_list_correction and corrections:
601603
apply_test_list_corrections(test_list, corrections, items,
602604
test_prefix)

tests/integration/defs/test_unittests.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ def test_unittests_v2(llm_root, llm_venv, case: str, output_dir, request):
7676
else:
7777
test_prefix = "unittest"
7878

79+
waives_file = request.config.getoption("--waives-file")
80+
7981
num_workers = 1
8082

8183
# This dataframe is not manually edited. Infra team will regularly generate this dataframe based on test execution results.
@@ -132,9 +134,13 @@ def test_unittests_v2(llm_root, llm_venv, case: str, output_dir, request):
132134
if dry_run:
133135
command += ['--collect-only']
134136

137+
if waives_file:
138+
waives_file = os.path.abspath(waives_file)
139+
command += [f"--waives-file={waives_file}"]
140+
135141
command += arg_list
136142

137-
print(f"Running unit test:'{command}'")
143+
print(f"Running unit test:\"python {' '.join(command)}\"")
138144

139145
def run_command(cmd, num_workers=1):
140146
try:

tests/unittest/conftest.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import os
1717
import sys
1818
import traceback
19+
from functools import partial
1920
from typing import Any
2021

2122
import _pytest.outcomes
@@ -24,6 +25,9 @@
2425
import tqdm
2526
from mpi4py.futures import MPIPoolExecutor
2627

28+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
29+
from integration.defs import test_list_parser
30+
2731

2832
def pytest_configure(config):
2933
# avoid thread leak of tqdm's TMonitor
@@ -87,11 +91,84 @@ def pytest_addoption(parser):
8791
default=False,
8892
help="Run Ray-marked tests (by default they are skipped).",
8993
)
94+
parser.addoption(
95+
"--waives-file",
96+
"-S",
97+
action="store",
98+
default=None,
99+
help=
100+
"Specify a file containing a list of waives, one per line. After filtering collected tests, Pytest will "
101+
"apply the waive state specified by this file to the set of tests to be run.",
102+
)
103+
104+
105+
def apply_waives_ut(waives_file, items: list[pytest.Item], config):
106+
"""Apply waives based on the waive state specified by the given waives_file."""
107+
108+
# Corrections don't make sense for the waives file as it specifies global negative
109+
# filters that may or may not be applicable to the current platform (i.e., the test names
110+
# being waived may not be generated on the current platform).
111+
try:
112+
parse_test_list_lines_bak = test_list_parser.parse_test_list_lines
113+
test_list_parser.parse_test_list_lines = partial(
114+
test_list_parser.parse_test_list_lines, convert_unittest=False)
115+
ret = test_list_parser.parse_and_validate_test_list(
116+
waives_file,
117+
config,
118+
items,
119+
check_for_corrections=False,
120+
)
121+
finally:
122+
test_list_parser.parse_test_list_lines = parse_test_list_lines_bak
123+
if not ret:
124+
return
125+
_, test_name_to_marker_dict = ret
126+
127+
filtered_dict = {}
128+
for waiver in test_name_to_marker_dict.keys():
129+
if "unittest/" not in waiver:
130+
continue
131+
elif "unittest/unittest/" in waiver:
132+
filtered_dict[waiver.replace(
133+
"unittest/unittest/",
134+
"unittest/")] = test_name_to_marker_dict[waiver]
135+
else:
136+
filtered_dict[waiver] = test_name_to_marker_dict[waiver]
137+
138+
# Fuzzy match is supported in the following order:
139+
# 1. exact match
140+
# 2. remove parameterization part in square brackets and try again (function level)
141+
# 3. remove the last part after '::' and try again, until no '::' left (file level)
142+
# Note: directory level match is not supported.
143+
def match_waiver(id: str):
144+
if id in filtered_dict:
145+
return filtered_dict[id]
146+
if id.endswith("]"):
147+
id = id.split("[")[0]
148+
if id in filtered_dict:
149+
return filtered_dict[id]
150+
while "::" in id:
151+
id = id.rsplit("::", 1)[0]
152+
if id in filtered_dict:
153+
return filtered_dict[id]
154+
155+
return None
156+
157+
# For each item in the list, apply waives if a waive entry exists
158+
for item in items:
159+
waiver = match_waiver(item.nodeid)
160+
if waiver:
161+
marker, reason, _ = waiver
162+
if marker:
163+
mark_func = getattr(pytest.mark, marker.lower())
164+
mark = mark_func(reason=reason)
165+
item.add_marker(mark)
90166

91167

92168
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
93169
def pytest_collection_modifyitems(session, config, items):
94170
test_prefix = config.getoption("--test-prefix")
171+
waives_file = config.getoption("--waives-file")
95172

96173
yield
97174

@@ -102,6 +179,8 @@ def pytest_collection_modifyitems(session, config, items):
102179
for item in items:
103180
item._nodeid = f"{test_prefix}/{item._nodeid}"
104181

182+
if waives_file:
183+
apply_waives_ut(waives_file, items, config)
105184
# Ray tests are disabled by default
106185
run_ray = config.getoption("--run-ray") or os.environ.get(
107186
"TLLM_RUN_RAY_TESTS") == "1"

0 commit comments

Comments
 (0)