1616import os
1717import sys
1818import traceback
19+ from functools import partial
1920from typing import Any
2021
2122import _pytest .outcomes
2425import tqdm
2526from 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
2832def 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 )
93169def 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