88import sys
99import time
1010from pathlib import Path
11- from typing import List , TextIO , Union , Iterator
11+ from typing import List , TextIO , Union , Iterator , Optional
1212from collections import defaultdict
1313
1414from pavilion import config
2222from pavilion .errors import TestRunError , CommandError , TestSeriesError , \
2323 PavilionError , TestGroupError
2424from pavilion .test_run import TestRun , load_tests , TestAttributes
25+ from pavilion .test_ids import TestID , SeriesID
2526from pavilion .types import ID_Pair
2627from pavilion .micro import flatten
2728
2829LOGGER = logging .getLogger (__name__ )
2930
3031
31- def expand_range (test_range : str ) -> List [str ]:
32- """Expand a given test or series range into a list of the individual
33- tests or series in that range"""
34-
35- tests = []
36-
37- if test_range == "all" :
38- return ["all" ]
39-
40- elif '-' in test_range :
41- id_start , id_end = test_range .split ('-' , 1 )
42-
43- if id_start .startswith ('s' ):
44- series_range_start = int (id_start .replace ('s' ,'' ))
45-
46- if id_end .startswith ('s' ):
47- series_range_end = int (id_end .replace ('s' ,'' ))
48- else :
49- series_range_end = int (id_end )
50-
51- series_ids = range (series_range_start , series_range_end + 1 )
52-
53- for sid in series_ids :
54- tests .append ('s' + str (sid ))
55- else :
56- test_range_start = int (id_start )
57- test_range_end = int (id_end )
58- test_ids = range (test_range_start , test_range_end + 1 )
59-
60- for tid in test_ids :
61- tests .append (str (tid ))
62- else :
63- tests .append (test_range )
64-
65- return tests
66-
67-
68- def expand_ranges (ranges : Iterator [str ]) -> Iterator [str ]:
69- """Given a sequence of test and series ranges, expand them
70- into a sequence of individual tests and series."""
71-
72- return flatten (map (expand_range , ranges ))
73-
74-
75- #pylint: disable=C0103
76- def is_series_id (id : str ) -> bool :
77- """Determine whether the given ID is a series ID."""
78-
79- return len (id ) > 0 and id [0 ].lower () == 's'
80-
81-
8232def load_last_series (pav_cfg , errfile : TextIO ) -> Union [series .TestSeries , None ]:
8333 """Load the series object for the last series run by this user on this system."""
8434
8535 try :
8636 series_id = series .load_user_series_id (pav_cfg )
8737 except series .TestSeriesError as err :
88- output .fprint ("Failed to find last series: {}" .format (err .args [0 ]), file = errfile )
38+ output .fprint (errfile , "Failed to find last series: {}" .format (err .args [0 ]))
39+ return None
40+
41+ if series_id is None :
42+ output .fprint (errfile , "Failed to find last series." )
8943 return None
9044
9145 try :
92- return series .TestSeries .load (pav_cfg , series_id )
46+ return series .TestSeries .load (pav_cfg , series_id . id_str )
9347 except series .TestSeriesError as err :
9448 output .fprint (errfile , "Failed to load last series: {}" .format (err .args [0 ]))
9549 return None
@@ -133,32 +87,14 @@ def arg_filtered_tests(pav_cfg: "PavConfig", args: argparse.Namespace,
13387 sys_name = getattr (args , 'sys_name' , sys_vars .get_vars (defer = True ).get ('sys_name' ))
13488 sort_by = getattr (args , 'sort_by' , 'created' )
13589
136- ids = []
137-
138- for test_range in args .tests :
139- ids .extend (expand_range (test_range ))
140-
141- args .tests = ids
142-
14390 has_filter_defaults = False
14491
14592 for arg , default in filters .TEST_FILTER_DEFAULTS .items ():
14693 if hasattr (args , arg ) and default != getattr (args , arg ):
14794 has_filter_defaults = True
14895 break
14996
150- # "all" takes priority over everything else
151- if "all" in args .tests :
152- args .tests = ["all" ]
153- elif "last" in args .tests :
154- args .tests = ["last" ]
155- elif len (args .tests ) == 0 :
156- if has_filter_defaults or args .filter is not None :
157- args .tests = ["all" ]
158- else :
159- args .tests = ["last" ]
160-
161- if "all" in args .tests and args .filter is not None and not has_filter_defaults :
97+ if SeriesID ("all" ) in args .tests and args .filter is not None and not has_filter_defaults :
16298 output .fprint (verbose , "Using default search filters: The current system, user, and "
16399 "created less than 1 day ago." , color = output .CYAN )
164100 args .filter = make_filter_query ()
@@ -173,7 +109,7 @@ def arg_filtered_tests(pav_cfg: "PavConfig", args: argparse.Namespace,
173109
174110 order_func , order_asc = filters .get_sort_opts (sort_by , "TEST" )
175111
176- if "all" in args .tests :
112+ if SeriesID ( "all" ) in args .tests :
177113 tests = dir_db .SelectItems ([], [])
178114 working_dirs = set (map (lambda cfg : cfg ['working_dir' ],
179115 pav_cfg .configs .values ()))
@@ -235,10 +171,7 @@ def arg_filtered_series(pav_cfg: config.PavConfig, args: argparse.Namespace,
235171 limit = getattr (args , 'limit' , filters .SERIES_FILTER_DEFAULTS ['limit' ])
236172 verbose = verbose or io .StringIO ()
237173
238- if not args .series :
239- args .series = ['last' ]
240-
241- if 'all' in args .series :
174+ if SeriesID ('all' ) in args .series :
242175 for arg , default in filters .SERIES_FILTER_DEFAULTS .items ():
243176 if hasattr (args , arg ) and default != getattr (args , arg ):
244177 break
@@ -252,14 +185,14 @@ def arg_filtered_series(pav_cfg: config.PavConfig, args: argparse.Namespace,
252185 for sid in args .series :
253186 # Go through each provided sid (including last and all) and find all
254187 # matching series. Then only add them if we haven't seen them yet.
255- if sid == ' last' :
188+ if sid . last () :
256189 last_series = load_last_series (pav_cfg , verbose )
257190 if last_series is None :
258191 return []
259192
260193 found_series .append (last_series .info ())
261194
262- elif sid == ' all' :
195+ elif sid . all () :
263196 sort_by = getattr (args , 'sort_by' , filters .SERIES_FILTER_DEFAULTS ['sort_by' ])
264197 order_func , order_asc = filters .get_sort_opts (sort_by , 'SERIES' )
265198
@@ -283,7 +216,7 @@ def arg_filtered_series(pav_cfg: config.PavConfig, args: argparse.Namespace,
283216 limit = limit ,
284217 ).data
285218 else :
286- found_series .append (series .SeriesInfo .load (pav_cfg , sid ))
219+ found_series .append (series .SeriesInfo .load (pav_cfg , sid . id_str ))
287220
288221 matching_series = []
289222 for sinfo in found_series :
@@ -358,20 +291,16 @@ def test_list_to_paths(pav_cfg, req_tests, errfile=None) -> List[Path]:
358291 test_paths = []
359292 for raw_id in req_tests :
360293
361- if raw_id == ' last' :
294+ if isinstance ( raw_id , SeriesID ) and raw_id . last () :
362295 raw_id = series .load_user_series_id (pav_cfg , errfile )
363296 if raw_id is None :
364297 output .fprint (errfile , "User has no 'last' series for this machine." ,
365298 color = output .YELLOW )
366299 continue
367300
368- if raw_id is None or not raw_id :
369- continue
370-
371- if '.' in raw_id or utils .is_int (raw_id ):
372- # This is a test id.
301+ if isinstance (raw_id , TestID ):
373302 try :
374- test_wd , _id = TestRun .parse_raw_id (pav_cfg , raw_id )
303+ test_wd , _id = TestRun .parse_raw_id (pav_cfg , raw_id . id_str )
375304 except TestRunError as err :
376305 output .fprint (errfile , err , color = output .YELLOW )
377306 continue
@@ -382,18 +311,17 @@ def test_list_to_paths(pav_cfg, req_tests, errfile=None) -> List[Path]:
382311 output .fprint (errfile ,
383312 "Test run with id '{}' could not be found." .format (raw_id ),
384313 color = output .YELLOW )
385- elif raw_id [0 ] == 's' and utils .is_int (raw_id [1 :]):
386- # A series.
314+ elif isinstance (raw_id , SeriesID ):
387315 try :
388316 test_paths .extend (
389- series .list_series_tests (pav_cfg , raw_id ))
317+ series .list_series_tests (pav_cfg , raw_id . id_str ))
390318 except TestSeriesError :
391319 output .fprint (errfile , "Invalid series id '{}'" .format (raw_id ),
392320 color = output .YELLOW )
393321 else :
394322 # A group
395323 try :
396- group = groups .TestGroup (pav_cfg , raw_id )
324+ group = groups .TestGroup (pav_cfg , raw_id . id_str )
397325 except TestGroupError as err :
398326 output .fprint (
399327 errfile ,
@@ -419,30 +347,21 @@ def test_list_to_paths(pav_cfg, req_tests, errfile=None) -> List[Path]:
419347
420348
421349def _filter_tests_by_raw_id (pav_cfg , id_pairs : List [ID_Pair ],
422- exclude_ids : List [str ]) -> List [ID_Pair ]:
350+ exclude_ids : List [TestID ]) -> List [ID_Pair ]:
423351 """Filter the given tests by raw id."""
424352
425353 exclude_pairs = []
426354
427355 for raw_id in exclude_ids :
428- if '.' in raw_id :
429- label , ex_id = raw_id .split ('.' , 1 )
430- else :
431- label = 'main'
432- ex_id = raw_id
356+ label = raw_id .label
357+ ex_id = raw_id .test_num
433358
434359 ex_wd = pav_cfg ['configs' ].get (label , None )
435360 if ex_wd is None :
436361 # Invalid label.
437362 continue
438363
439364 ex_wd = Path (ex_wd )
440-
441- try :
442- ex_id = int (ex_id )
443- except ValueError :
444- continue
445-
446365 exclude_pairs .append ((ex_wd , ex_id ))
447366
448367 return [pair for pair in id_pairs if pair not in exclude_pairs ]
@@ -483,8 +402,8 @@ def get_tests_by_paths(pav_cfg, test_paths: List[Path], errfile: TextIO,
483402 return load_tests (pav_cfg , test_pairs , errfile )
484403
485404
486- def get_tests_by_id (pav_cfg , test_ids : List ['str' ], errfile : TextIO ,
487- exclude_ids : List [str ] = None ) -> List [TestRun ]:
405+ def get_tests_by_id (pav_cfg , test_ids : List [Union [ TestID , SeriesID ] ], errfile : TextIO ,
406+ exclude_ids : List [TestID ] = None ) -> List [TestRun ]:
488407 """Convert a list of raw test id's and series id's into a list of
489408 test objects.
490409
@@ -495,23 +414,16 @@ def get_tests_by_id(pav_cfg, test_ids: List['str'], errfile: TextIO,
495414 :return: List of test objects
496415 """
497416
498- test_ids = [str (test ) for test in test_ids .copy ()]
499-
500- if not test_ids :
501- # Get the last series ran by this user
502- series_id = series .load_user_series_id (pav_cfg )
503- if series_id is not None :
504- test_ids .append (series_id )
505- else :
506- raise CommandError ("No tests specified and no last series was found." )
507-
508417 # Convert series and test ids into test paths.
509418 test_id_pairs = []
510419 for raw_id in test_ids :
511420 # Series start with 's' (like 'snake') and never have labels
512- if '.' not in raw_id and raw_id . startswith ( 's' ):
421+ if isinstance ( raw_id , SeriesID ):
513422 try :
514- series_obj = series .TestSeries .load (pav_cfg , raw_id )
423+ if raw_id .last ():
424+ series_obj = load_last_series (pav_cfg , errfile )
425+ else :
426+ series_obj = series .TestSeries .load (pav_cfg , raw_id .id_str )
515427 except TestSeriesError as err :
516428 output .fprint (errfile , "Suite {} could not be found.\n {}"
517429 .format (raw_id , err ), color = output .RED )
@@ -521,7 +433,7 @@ def get_tests_by_id(pav_cfg, test_ids: List['str'], errfile: TextIO,
521433 # Just a plain test id.
522434 else :
523435 try :
524- test_id_pairs .append (TestRun .parse_raw_id (pav_cfg , raw_id ))
436+ test_id_pairs .append (TestRun .parse_raw_id (pav_cfg , raw_id . id_str ))
525437
526438 except TestRunError as err :
527439 output .fprint (sys .stdout , "Error loading test '{}': {}"
@@ -600,3 +512,29 @@ def get_glob(test_suite_name, test_names):
600512
601513 testset_name = ',' .join (globs ).rstrip (',' )
602514 return testset_name
515+
516+
517+ def get_last_test_id (pav_cfg : "PavConfig" , errfile : TextIO ) -> Optional [TestID ]:
518+ """Get the ID of the last run test, if it exists, and if there is a single
519+ unambigous last test. If there is not, return None."""
520+
521+ last_series = load_last_series (pav_cfg , errfile )
522+
523+ if last_series is None :
524+ return None
525+
526+ test_ids = list (last_series .tests .keys ())
527+
528+ if len (test_ids ) == 0 :
529+ output .fprint (
530+ errfile ,
531+ f"Most recent series contains no tests." )
532+ return None
533+
534+ if len (test_ids ) > 1 :
535+ output .fprint (
536+ errfile ,
537+ f"Multiple tests exist in last series. Could not unambiguously identify last test." )
538+ return None
539+
540+ return TestID (test_ids [0 ])
0 commit comments