@@ -144,18 +144,73 @@ def _sanitize_name(self, s: str) -> str:
144144 """Sanitize names for filenames: keep alphanumeric, dot, dash, underscore."""
145145 return re .sub (r"[^A-Za-z0-9_.-]" , "_" , s )
146146
147+ @staticmethod
148+ def _escape_nextest_filter_matcher (matcher : str ) -> str :
149+ return matcher .replace ("\\ " , "\\ \\ " ).replace (")" , "\\ )" )
150+
151+ @staticmethod
152+ def _quote_bash_arg (arg : str ) -> str :
153+ escaped = (
154+ arg .replace ("\\ " , "\\ \\ " )
155+ .replace ('"' , '\\ "' )
156+ .replace ("$" , "\\ $" )
157+ .replace ("`" , "\\ `" )
158+ )
159+ return f'"{ escaped } "'
160+
161+ def _build_nextest_filterset (
162+ self ,
163+ base_filter : str ,
164+ only : Optional [List [str ]],
165+ skip : Optional [List [str ]],
166+ ) -> str :
167+ filterset = f"test(~{ self ._escape_nextest_filter_matcher (base_filter )} )"
168+
169+ if only :
170+ included_tests = "|" .join (
171+ f"test(={ self ._escape_nextest_filter_matcher (test_name )} )"
172+ for test_name in only
173+ )
174+ filterset = f"({ filterset } &({ included_tests } ))"
175+
176+ if skip :
177+ for skipped_test in skip :
178+ skipped_filter = self ._escape_nextest_filter_matcher (skipped_test )
179+ filterset = f"{ filterset } -test(={ skipped_filter } )"
180+
181+ return f"--filterset={ filterset } "
182+
147183 def _prepare_subtests (
148184 self ,
149185 test_type : str ,
150186 hypervisor : str ,
151187 only : Optional [List [str ]],
152188 skip : Optional [List [str ]],
189+ cli_test_type : Optional [str ] = None ,
190+ only_test_prefixes : Optional [List [str ]] = None ,
153191 ) -> Dict [str , Any ]:
154192 """Prepare subtests and skip arguments."""
155- subtests = self ._list_subtests (hypervisor , test_type )
193+ subtests = self ._list_subtests (hypervisor , cli_test_type or test_type )
156194 # Store the ordered list for diagnostic purposes
157195 self ._ordered_subtests = subtests .copy ()
158196
197+ if only_test_prefixes is not None :
198+ prefixed_subtests = [
199+ subtest
200+ for subtest in subtests
201+ if any (subtest .startswith (prefix ) for prefix in only_test_prefixes )
202+ ]
203+ if not prefixed_subtests :
204+ fail (
205+ f"No Cloud Hypervisor { test_type } subtests matched prefixes "
206+ f"{ only_test_prefixes } . Verify the selected Cloud Hypervisor "
207+ "ref contains those integration tests."
208+ )
209+ if only is None :
210+ only = prefixed_subtests
211+ else :
212+ only = [subtest for subtest in only if subtest in prefixed_subtests ]
213+
159214 if only is not None :
160215 if not skip :
161216 skip = []
@@ -409,16 +464,46 @@ def run_tests(
409464 ref : str = "" ,
410465 only : Optional [List [str ]] = None ,
411466 skip : Optional [List [str ]] = None ,
467+ cli_test_type : Optional [str ] = None ,
468+ only_test_prefixes : Optional [List [str ]] = None ,
469+ cli_test_filter : Optional [str ] = None ,
412470 ) -> None :
471+ cli_test_type = cli_test_type or test_type
472+
413473 if ref :
414474 self .node .tools [Git ].checkout (ref , self .repo_root )
415475
416- subtests = self ._prepare_subtests (test_type , hypervisor , only , skip )
476+ if cli_test_filter :
477+ subtests : Dict [str , Any ] = {"subtest_set" : set (), "skip_args" : "" }
478+ else :
479+ subtests = self ._prepare_subtests (
480+ test_type ,
481+ hypervisor ,
482+ only ,
483+ skip ,
484+ cli_test_type ,
485+ only_test_prefixes ,
486+ )
417487 self ._configure_environment_if_needed (hypervisor )
418488
419489 # Use enhanced diagnostics for better debugging and monitoring
420490 skip_args = subtests ["skip_args" ]
421- cmd_args = f"tests --hypervisor { hypervisor } --{ test_type } -- -- { skip_args } "
491+ if cli_test_filter :
492+ nextest_filter = self ._build_nextest_filterset (
493+ cli_test_filter ,
494+ only ,
495+ skip ,
496+ )
497+ test_script_args = f"--test-filter { self ._quote_bash_arg (nextest_filter )} "
498+ cmd_args = (
499+ f"tests --hypervisor { hypervisor } --{ cli_test_type } -- "
500+ f"{ test_script_args } "
501+ )
502+ else :
503+ cmd_args = (
504+ f"tests --hypervisor { hypervisor } --{ cli_test_type } -- -- "
505+ f"{ skip_args } "
506+ )
422507 # normalize name so artifacts are predictable (no spaces/colons/slashes)
423508 safe_test_type = self ._sanitize_name (test_type .replace ("-" , "_" ))
424509 test_name = self ._sanitize_name (f"ch_{ safe_test_type } _{ hypervisor } " )
@@ -1379,8 +1464,8 @@ def _cache_vmm_version(self) -> None:
13791464 except Exception as e :
13801465 self ._log .debug (f"Could not cache VMM version: { e } " )
13811466
1382- def _list_subtests (self , hypervisor : str , test_type : str ) -> List [str ]:
1383- cmd_args = f"tests --hypervisor { hypervisor } --{ test_type } -- -- --list"
1467+ def _list_subtests (self , hypervisor : str , cli_test_type : str ) -> List [str ]:
1468+ cmd_args = f"tests --hypervisor { hypervisor } --{ cli_test_type } -- -- --list"
13841469 # Use enhanced environment variables for consistency
13851470 enhanced_env_vars = self .env_vars .copy ()
13861471 enhanced_env_vars .update (
0 commit comments