44# SPDX-License-Identifier: BSD-3-Clause
55
66import decimal
7+ import collections
78import functools
89import inspect
910import json
@@ -246,8 +247,8 @@ class RunReport:
246247 '''
247248 def __init__ (self ):
248249 # Initialize the report with the required fields
249- self .__filename = None
250- self .__report = {
250+ self ._filename = None
251+ self ._report = {
251252 'session_info' : {
252253 'data_version' : DATA_VERSION ,
253254 'hostname' : socket .gethostname (),
@@ -261,16 +262,16 @@ def __init__(self):
261262
262263 @property
263264 def filename (self ):
264- return self .__filename
265+ return self ._filename
265266
266267 def __getattr__ (self , name ):
267- return getattr (self .__report , name )
268+ return getattr (self ._report , name )
268269
269270 def __getitem__ (self , key ):
270- return self .__report [key ]
271+ return self ._report [key ]
271272
272273 def __rfm_json_encode__ (self ):
273- return self .__report
274+ return self ._report
274275
275276 @classmethod
276277 def create_from_perflog (cls , * logfiles , format = None ,
@@ -393,23 +394,60 @@ def _convert(x):
393394 'run_index' : run_index ,
394395 'testcases' : testcases
395396 })
396- return report
397+ return [report ]
398+
399+ @classmethod
400+ def create_from_sqlite_db (cls , * dbfiles , exclude_sessions = None ,
401+ include_sessions = None , time_period = None ):
402+ dst_backend = StorageBackend .default ()
403+ dst_schema = dst_backend .schema_version ()
404+ if not time_period :
405+ time_period = {'start' : '19700101T0000+0000' , 'end' : 'now' }
406+
407+ start = time_period .get ('start' , '19700101T0000+0000' )
408+ end = time_period .get ('end' , 'now' )
409+ ts_start , ts_end = parse_time_period (f'{ start } :{ end } ' )
410+ include_sessions = set (include_sessions ) if include_sessions else set ()
411+ exclude_sessions = set (exclude_sessions ) if exclude_sessions else set ()
412+ reports = []
413+ for filename in dbfiles :
414+ src_backend = StorageBackend .create ('sqlite' , filename )
415+ src_schema = src_backend .schema_version ()
416+ if src_schema != dst_schema :
417+ getlogger ().warning (
418+ f'ignoring DB file { filename } : schema version mismatch: '
419+ f'cannot import from DB v{ src_schema } to v{ dst_schema } '
420+ )
421+ continue
422+
423+ sessions = src_backend .fetch_sessions_time_period (ts_start , ts_end )
424+ for sess in sessions :
425+ uuid = sess ['session_info' ]['uuid' ]
426+ if include_sessions and uuid not in include_sessions :
427+ continue
428+
429+ if exclude_sessions and uuid in exclude_sessions :
430+ continue
431+
432+ reports .append (_ImportedRunReport (sess ))
433+
434+ return reports
397435
398436 def _add_run (self , run ):
399- self .__report ['runs' ].append (run )
437+ self ._report ['runs' ].append (run )
400438
401439 def update_session_info (self , session_info ):
402440 # Remove timestamps
403441 for key , val in session_info .items ():
404442 if not key .startswith ('time_' ):
405- self .__report ['session_info' ][key ] = val
443+ self ._report ['session_info' ][key ] = val
406444
407445 def update_restored_cases (self , restored_cases , restored_session ):
408- self .__report ['restored_cases' ] = [restored_session .case (c )
409- for c in restored_cases ]
446+ self ._report ['restored_cases' ] = [restored_session .case (c )
447+ for c in restored_cases ]
410448
411449 def update_timestamps (self , ts_start , ts_end ):
412- self .__report ['session_info' ].update ({
450+ self ._report ['session_info' ].update ({
413451 'time_start' : time .strftime (_DATETIME_FMT ,
414452 time .localtime (ts_start )),
415453 'time_start_unix' : ts_start ,
@@ -426,10 +464,10 @@ def update_extras(self, extras):
426464 raise ValueError ('cannot use reserved keys '
427465 f'`{ "," .join (clashed_keys )} ` as session extras' )
428466
429- self .__report ['session_info' ].update (extras )
467+ self ._report ['session_info' ].update (extras )
430468
431469 def update_run_stats (self , stats ):
432- session_uuid = self .__report ['session_info' ]['uuid' ]
470+ session_uuid = self ._report ['session_info' ]['uuid' ]
433471 for runidx , tasks in stats .runs ():
434472 testcases = []
435473 num_failures = 0
@@ -530,7 +568,7 @@ def update_run_stats(self, stats):
530568
531569 testcases .append (entry )
532570
533- self .__report ['runs' ].append ({
571+ self ._report ['runs' ].append ({
534572 'num_cases' : len (tasks ),
535573 'num_failures' : num_failures ,
536574 'num_aborted' : num_aborted ,
@@ -540,23 +578,23 @@ def update_run_stats(self, stats):
540578 })
541579
542580 # Update session info from stats
543- self .__report ['session_info' ].update ({
544- 'num_cases' : self .__report ['runs' ][0 ]['num_cases' ],
545- 'num_failures' : self .__report ['runs' ][- 1 ]['num_failures' ],
546- 'num_aborted' : self .__report ['runs' ][- 1 ]['num_aborted' ],
547- 'num_skipped' : self .__report ['runs' ][- 1 ]['num_skipped' ]
581+ self ._report ['session_info' ].update ({
582+ 'num_cases' : self ._report ['runs' ][0 ]['num_cases' ],
583+ 'num_failures' : self ._report ['runs' ][- 1 ]['num_failures' ],
584+ 'num_aborted' : self ._report ['runs' ][- 1 ]['num_aborted' ],
585+ 'num_skipped' : self ._report ['runs' ][- 1 ]['num_skipped' ]
548586 })
549587
550588 def _save (self , filename , compress , link_to_last ):
551589 filename = _expand_report_filename (filename , newfile = True )
552590 with open (filename , 'w' ) as fp :
553591 if compress :
554- jsonext .dump (self .__report , fp )
592+ jsonext .dump (self ._report , fp )
555593 else :
556- jsonext .dump (self .__report , fp , indent = 2 )
594+ jsonext .dump (self ._report , fp , indent = 2 )
557595 fp .write ('\n ' )
558596
559- self .__filename = filename
597+ self ._filename = filename
560598 if not link_to_last :
561599 return
562600
@@ -576,7 +614,7 @@ def _save(self, filename, compress, link_to_last):
576614
577615 def is_empty (self ):
578616 '''Return :obj:`True` is no test cases where run'''
579- return self .__report ['session_info' ]['num_cases' ] == 0
617+ return self ._report ['session_info' ]['num_cases' ] == 0
580618
581619 def save (self , filename , compress = False , link_to_last = True ):
582620 prefix = os .path .dirname (filename ) or '.'
@@ -591,7 +629,7 @@ def store(self):
591629 def generate_xml_report (self ):
592630 '''Generate a JUnit report from a standard ReFrame JSON report.'''
593631
594- report = self .__report
632+ report = self ._report
595633 xml_testsuites = etree .Element ('testsuites' )
596634 # Create a XSD-friendly timestamp
597635 session_ts = time .strftime (
@@ -688,6 +726,30 @@ def __missing__(self, key):
688726 raise KeyError (key )
689727
690728
729+ class _ImportedRunReport (RunReport ):
730+ def __init__ (self , report ):
731+ self ._filename = f'{ report ["session_info" ]["uuid" ]} .json'
732+ self ._report = report
733+
734+ def _add_run (self , run ):
735+ raise NotImplementedError
736+
737+ def update_session_info (self , session_info ):
738+ raise NotImplementedError
739+
740+ def update_restored_cases (self , restored_cases , restored_session ):
741+ raise NotImplementedError
742+
743+ def update_timestamps (self , ts_start , ts_end ):
744+ raise NotImplementedError
745+
746+ def update_extras (self , extras ):
747+ raise NotImplementedError
748+
749+ def update_run_stats (self , stats ):
750+ raise NotImplementedError
751+
752+
691753def _group_key (groups , testcase : _TCProxy ):
692754 key = []
693755 for grp in groups :
0 commit comments